aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2015-01-27 00:17:24 -0300
committerFabián Ezequiel Gallina2015-01-27 00:17:24 -0300
commit5485e3e5b28f82b46d139c63b8ab77ed1d7d61c9 (patch)
treef914aa2e95c6178c437bbc472708a72e388cbc96 /lisp/progmodes/python.el
parent3b23e6a70294a3fee23353bfb8a23c7167d7c4ef (diff)
downloademacs-5485e3e5b28f82b46d139c63b8ab77ed1d7d61c9.tar.gz
emacs-5485e3e5b28f82b46d139c63b8ab77ed1d7d61c9.zip
python.el: New non-global state dependent indentation engine.
Fixes: debbugs:18319 Fixes: debbugs:19595 * lisp/progmodes/python.el (python-syntax-comment-or-string-p): Accept PPSS as argument. (python-syntax-closing-paren-p): New function. (python-indent-current-level) (python-indent-levels): Mark obsolete. (python-indent-context): Return more context cases. (python-indent--calculate-indentation) (python-indent--calculate-levels): New functions. (python-indent-calculate-levels): Use them. (python-indent-calculate-indentation, python-indent-line): (python-indent-line-function): Rewritten to use new API. (python-indent-dedent-line): Simplify logic. (python-indent-dedent-line-backspace): Use `unless`. (python-indent-toggle-levels): Delete function. * test/automated/python-tests.el (python-indent-pep8-1) (python-indent-pep8-2, python-indent-pep8-3) (python-indent-after-comment-1, python-indent-after-comment-2) (python-indent-inside-paren-1, python-indent-inside-paren-2) (python-indent-after-block-1, python-indent-after-block-2) (python-indent-after-backslash-1, python-indent-after-backslash-2) (python-indent-after-backslash-3, python-indent-block-enders-1) (python-indent-block-enders-2, python-indent-block-enders-3) (python-indent-block-enders-4, python-indent-block-enders-5) (python-indent-dedenters-1, python-indent-dedenters-2) (python-indent-dedenters-3, python-indent-dedenters-4) (python-indent-dedenters-5, python-indent-dedenters-6) (python-indent-dedenters-7, python-indent-dedenters-8): Fix tests. (python-indent-base-case, python-indent-after-block-3) (python-indent-after-backslash-5, python-indent-inside-paren-3) (python-indent-inside-paren-4, python-indent-inside-paren-5) (python-indent-inside-paren-6, python-indent-inside-string-1) (python-indent-inside-string-2, python-indent-inside-string-3) (python-indent-dedent-line-backspace-1): New Tests.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el695
1 files changed, 360 insertions, 335 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 833c3d9a9c5..d0a83087554 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -447,9 +447,14 @@ The type returned can be `comment', `string' or `paren'."
447 ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string)) 447 ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string))
448 ((nth 1 ppss) 'paren)))) 448 ((nth 1 ppss) 'paren))))
449 449
450(defsubst python-syntax-comment-or-string-p () 450(defsubst python-syntax-comment-or-string-p (&optional ppss)
451 "Return non-nil if point is inside 'comment or 'string." 451 "Return non-nil if PPSS is inside 'comment or 'string."
452 (nth 8 (syntax-ppss))) 452 (nth 8 (or ppss (syntax-ppss))))
453
454(defsubst python-syntax-closing-paren-p ()
455 "Return non-nil if char after point is a closing paren."
456 (= (syntax-class (syntax-after (point)))
457 (syntax-class (string-to-syntax ")"))))
453 458
454(define-obsolete-function-alias 459(define-obsolete-function-alias
455 'python-info-ppss-context #'python-syntax-context "24.3") 460 'python-info-ppss-context #'python-syntax-context "24.3")
@@ -671,10 +676,28 @@ It makes underscores and dots word constituent chars.")
671 'python-guess-indent 'python-indent-guess-indent-offset "24.3") 676 'python-guess-indent 'python-indent-guess-indent-offset "24.3")
672 677
673(defvar python-indent-current-level 0 678(defvar python-indent-current-level 0
674 "Current indentation level `python-indent-line-function' is using.") 679 "Deprecated var available for compatibility.")
675 680
676(defvar python-indent-levels '(0) 681(defvar python-indent-levels '(0)
677 "Levels of indentation available for `python-indent-line-function'.") 682 "Deprecated var available for compatibility.")
683
684(make-obsolete-variable
685 'python-indent-current-level
686 "The indentation API changed to avoid global state.
687The function `python-indent-calculate-levels' does not use it
688anymore. If you were defadvising it and or depended on this
689variable for indentation customizations, refactor your code to
690work on `python-indent-calculate-indentation' instead."
691 "24.5")
692
693(make-obsolete-variable
694 'python-indent-levels
695 "The indentation API changed to avoid global state.
696The function `python-indent-calculate-levels' does not use it
697anymore. If you were defadvising it and or depended on this
698variable for indentation customizations, refactor your code to
699work on `python-indent-calculate-indentation' instead."
700 "24.5")
678 701
679(defun python-indent-guess-indent-offset () 702(defun python-indent-guess-indent-offset ()
680 "Guess and set `python-indent-offset' for the current buffer." 703 "Guess and set `python-indent-offset' for the current buffer."
@@ -714,356 +737,358 @@ It makes underscores and dots word constituent chars.")
714 python-indent-offset))))))) 737 python-indent-offset)))))))
715 738
716(defun python-indent-context () 739(defun python-indent-context ()
717 "Get information on indentation context. 740 "Get information about the current indentation context.
718Context information is returned with a cons with the form: 741Context is returned in a cons with the form (STATUS . START).
719 (STATUS . START) 742
720 743STATUS can be one of the following:
721Where status can be any of the following symbols: 744
722 745keyword
723 * after-comment: When current line might continue a comment block 746-------
724 * inside-paren: If point in between (), {} or [] 747
725 * inside-string: If point is inside a string 748:after-comment
726 * after-backslash: Previous line ends in a backslash 749 - Point is after a comment line.
727 * after-beginning-of-block: Point is after beginning of block 750 - START is the position of the \"#\" character.
728 * after-line: Point is after normal line 751:inside-string
729 * dedenter-statement: Point is on a dedenter statement. 752 - Point is inside string.
730 * no-indent: Point is at beginning of buffer or other special case 753 - START is the position of the first quote that starts it.
731START is the buffer position where the sexp starts." 754:no-indent
755 - No possible indentation case matches.
756 - START is always zero.
757
758:inside-paren
759 - Fallback case when point is inside paren.
760 - START is the first non space char position *after* the open paren.
761:inside-paren-at-closing-nested-paren
762 - Point is on a line that contains a nested paren closer.
763 - START is the position of the open paren it closes.
764:inside-paren-at-closing-paren
765 - Point is on a line that contains a paren closer.
766 - START is the position of the open paren.
767:inside-paren-newline-start
768 - Point is inside a paren with items starting in their own line.
769 - START is the position of the open paren.
770:inside-paren-newline-start-from-block
771 - Point is inside a paren with items starting in their own line
772 from a block start.
773 - START is the position of the open paren.
774
775:after-backslash
776 - Fallback case when point is after backslash.
777 - START is the char after the position of the backslash.
778:after-backslash-assignment-continuation
779 - Point is after a backslashed assignment.
780 - START is the char after the position of the backslash.
781:after-backslash-block-continuation
782 - Point is after a backslashed block continuation.
783 - START is the char after the position of the backslash.
784:after-backslash-dotted-continuation
785 - Point is after a backslashed dotted continuation. Previous
786 line must contain a dot to align with.
787 - START is the char after the position of the backslash.
788:after-backslash-first-line
789 - First line following a backslashed continuation.
790 - START is the char after the position of the backslash.
791
792:after-block-end
793 - Point is after a line containing a block ender.
794 - START is the position where the ender starts.
795:after-block-start
796 - Point is after a line starting a block.
797 - START is the position where the block starts.
798:after-line
799 - Point is after a simple line.
800 - START is the position where the previous line starts.
801:at-dedenter-block-start
802 - Point is on a line starting a dedenter block.
803 - START is the position where the dedenter block starts."
732 (save-restriction 804 (save-restriction
733 (widen) 805 (widen)
734 (let ((ppss (save-excursion (beginning-of-line) (syntax-ppss))) 806 (let ((ppss (save-excursion
735 (start)) 807 (beginning-of-line)
736 (cons 808 (syntax-ppss))))
737 (cond 809 (cond
738 ;; Beginning of buffer 810 ;; Beginning of buffer.
739 ((save-excursion 811 ((= (line-number-at-pos) 1)
740 (goto-char (line-beginning-position)) 812 (cons :no-indent 0))
741 (bobp)) 813 ;; Comment continuation (maybe).
742 'no-indent) 814 ((save-excursion
743 ;; Comment continuation 815 (when (and
744 ((save-excursion 816 (or
745 (when (and 817 (python-info-current-line-comment-p)
746 (or 818 (python-info-current-line-empty-p))
747 (python-info-current-line-comment-p) 819 (forward-comment -1)
748 (python-info-current-line-empty-p)) 820 (python-info-current-line-comment-p))
749 (progn 821 (cons :after-comment (point)))))
750 (forward-comment -1) 822 ;; Inside a string.
751 (python-info-current-line-comment-p))) 823 ((let ((start (python-syntax-context 'string ppss)))
752 (setq start (point)) 824 (when start
753 'after-comment))) 825 (cons :inside-string start))))
754 ;; Inside string 826 ;; Inside a paren.
755 ((setq start (python-syntax-context 'string ppss)) 827 ((let* ((start (python-syntax-context 'paren ppss))
756 'inside-string) 828 (starts-in-newline
757 ;; Inside a paren 829 (when start
758 ((setq start (python-syntax-context 'paren ppss)) 830 (save-excursion
759 'inside-paren) 831 (goto-char start)
760 ;; After backslash 832 (forward-char)
761 ((setq start (when (not (or (python-syntax-context 'string ppss) 833 (not
762 (python-syntax-context 'comment ppss))) 834 (= (line-number-at-pos)
763 (let ((line-beg-pos (line-number-at-pos))) 835 (progn
764 (python-info-line-ends-backslash-p 836 (python-util-forward-comment)
765 (1- line-beg-pos))))) 837 (line-number-at-pos))))))))
766 'after-backslash) 838 (when start
767 ;; After beginning of block 839 (cond
768 ((setq start (save-excursion 840 ;; Current line only holds the closing paren.
769 (when (progn 841 ((save-excursion
770 (back-to-indentation) 842 (skip-syntax-forward " ")
771 (python-util-forward-comment -1) 843 (when (and (python-syntax-closing-paren-p)
772 (equal (char-before) ?:)) 844 (progn
773 ;; Move to the first block start that's not in within 845 (forward-char 1)
774 ;; a string, comment or paren and that's not a 846 (not (python-syntax-context 'paren))))
775 ;; continuation line. 847 (cons :inside-paren-at-closing-paren start))))
776 (while (and (re-search-backward 848 ;; Current line only holds a closing paren for nested.
777 (python-rx block-start) nil t) 849 ((save-excursion
778 (or 850 (back-to-indentation)
779 (python-syntax-context-type) 851 (python-syntax-closing-paren-p))
780 (python-info-continuation-line-p)))) 852 (cons :inside-paren-at-closing-nested-paren start))
781 (when (looking-at (python-rx block-start)) 853 ;; This line starts from a opening block in its own line.
782 (point-marker))))) 854 ((save-excursion
783 'after-beginning-of-block) 855 (goto-char start)
784 ((when (setq start (python-info-dedenter-statement-p)) 856 (when (and
785 'dedenter-statement)) 857 starts-in-newline
786 ;; After normal line 858 (save-excursion
787 ((setq start (save-excursion 859 (back-to-indentation)
860 (looking-at (python-rx block-start))))
861 (cons
862 :inside-paren-newline-start-from-block start))))
863 (starts-in-newline
864 (cons :inside-paren-newline-start start))
865 ;; General case.
866 (t (cons :inside-paren
867 (save-excursion
868 (goto-char (1+ start))
869 (skip-syntax-forward "(" 1)
870 (skip-syntax-forward " ")
871 (point))))))))
872 ;; After backslash.
873 ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
874 (python-info-line-ends-backslash-p
875 (1- (line-number-at-pos))))))
876 (when start
877 (cond
878 ;; Continuation of dotted expression.
879 ((save-excursion
880 (back-to-indentation)
881 (when (eq (char-after) ?\.)
882 ;; Move point back until it's not inside a paren.
883 (while (prog2
884 (forward-line -1)
885 (and (not (bobp))
886 (python-syntax-context 'paren))))
887 (goto-char (line-end-position))
888 (while (and (search-backward
889 "." (line-beginning-position) t)
890 (python-syntax-context-type)))
891 ;; Ensure previous statement has dot to align with.
892 (when (and (eq (char-after) ?\.)
893 (not (python-syntax-context-type)))
894 (cons :after-backslash-dotted-continuation (point))))))
895 ;; Continuation of block definition.
896 ((let ((block-continuation-start
897 (python-info-block-continuation-line-p)))
898 (when block-continuation-start
899 (save-excursion
900 (goto-char block-continuation-start)
901 (re-search-forward
902 (python-rx block-start (* space))
903 (line-end-position) t)
904 (cons :after-backslash-block-continuation (point))))))
905 ;; Continuation of assignment.
906 ((let ((assignment-continuation-start
907 (python-info-assignment-continuation-line-p)))
908 (when assignment-continuation-start
909 (save-excursion
910 (goto-char assignment-continuation-start)
911 (cons :after-backslash-assignment-continuation (point))))))
912 ;; First line after backslash continuation start.
913 ((save-excursion
914 (goto-char start)
915 (when (or (= (line-number-at-pos) 1)
916 (not (python-info-beginning-of-backslash
917 (1- (line-number-at-pos)))))
918 (cons :after-backslash-first-line start))))
919 ;; General case.
920 (t (cons :after-backslash start))))))
921 ;; After beginning of block.
922 ((let ((start (save-excursion
923 (back-to-indentation)
924 (python-util-forward-comment -1)
925 (when (equal (char-before) ?:)
926 (python-nav-beginning-of-block)))))
927 (when start
928 (cons :after-block-start start))))
929 ;; At dedenter statement.
930 ((let ((start (python-info-dedenter-statement-p)))
931 (when start
932 (cons :at-dedenter-block-start start))))
933 ;; After normal line.
934 ((let ((start (save-excursion
788 (back-to-indentation) 935 (back-to-indentation)
789 (skip-chars-backward (rx (or whitespace ?\n))) 936 (skip-chars-backward " \t\n")
790 (python-nav-beginning-of-statement) 937 (python-nav-beginning-of-statement)
791 (point-marker))) 938 (point))))
792 'after-line) 939 (when start
793 ;; Do not indent 940 (if (save-excursion
794 (t 'no-indent)) 941 (python-util-forward-comment -1)
795 start)))) 942 (python-nav-beginning-of-statement)
796 943 (looking-at (python-rx block-ender)))
797(defun python-indent-calculate-indentation () 944 (cons :after-block-end start)
798 "Calculate correct indentation offset for the current line." 945 (cons :after-line start)))))
799 (let* ((indentation-context (python-indent-context)) 946 ;; Default case: do not indent.
800 (context-status (car indentation-context)) 947 (t (cons :no-indent 0))))))
801 (context-start (cdr indentation-context))) 948
802 (save-restriction 949(defun python-indent--calculate-indentation ()
803 (widen) 950 "Internal implementation of `python-indent-calculate-indentation'.
804 (save-excursion 951May return an integer for the maximum possible indentation at
805 (pcase context-status 952current context or a list of integers. The latter case is only
806 (`no-indent 0) 953happening for :at-dedenter-block-start context since the
807 (`after-comment 954possibilities can be narrowed to especific indentation points."
808 (goto-char context-start) 955 (save-restriction
809 (current-indentation)) 956 (widen)
810 ;; When point is after beginning of block just add one level 957 (save-excursion
811 ;; of indentation relative to the context-start 958 (pcase (python-indent-context)
812 (`after-beginning-of-block 959 (`(:no-indent . ,_) 0)
813 (goto-char context-start) 960 (`(,(or :after-line
814 (+ (current-indentation) python-indent-offset)) 961 :after-comment
815 ;; When after a simple line just use previous line 962 :inside-string
816 ;; indentation. 963 :after-backslash
817 (`after-line 964 :inside-paren-at-closing-paren
818 (let* ((pair (save-excursion 965 :inside-paren-at-closing-nested-paren) . ,start)
819 (goto-char context-start) 966 ;; Copy previous indentation.
820 (cons 967 (goto-char start)
821 (current-indentation) 968 (current-indentation))
822 (python-info-beginning-of-block-p)))) 969 (`(,(or :after-block-start
823 (context-indentation (car pair)) 970 :after-backslash-first-line
824 ;; TODO: Separate block enders into its own case. 971 :inside-paren-newline-start) . ,start)
825 (adjustment 972 ;; Add one indentation level.
826 (if (save-excursion 973 (goto-char start)
827 (python-util-forward-comment -1) 974 (+ (current-indentation) python-indent-offset))
828 (python-nav-beginning-of-statement) 975 (`(,(or :inside-paren
829 (looking-at (python-rx block-ender))) 976 :after-backslash-block-continuation
830 python-indent-offset 977 :after-backslash-assignment-continuation
831 0))) 978 :after-backslash-dotted-continuation) . ,start)
832 (- context-indentation adjustment))) 979 ;; Use the column given by the context.
833 ;; When point is on a dedenter statement, search for the 980 (goto-char start)
834 ;; opening block that corresponds to it and use its 981 (current-column))
835 ;; indentation. If no opening block is found just remove 982 (`(:after-block-end . ,start)
836 ;; indentation as this is an invalid python file. 983 ;; Subtract one indentation level.
837 (`dedenter-statement 984 (goto-char start)
838 (let ((block-start-point 985 (- (current-indentation) python-indent-offset))
839 (python-info-dedenter-opening-block-position))) 986 (`(:at-dedenter-block-start . ,_)
840 (save-excursion 987 ;; List all possible indentation levels from opening blocks.
841 (if (not block-start-point) 988 (let ((opening-block-start-points
842 0 989 (python-info-dedenter-opening-block-positions)))
843 (goto-char block-start-point) 990 (if (not opening-block-start-points)
844 (current-indentation))))) 991 0 ; if not found default to first column
845 ;; When inside of a string, do nothing. just use the current 992 (mapcar (lambda (pos)
846 ;; indentation. XXX: perhaps it would be a good idea to 993 (save-excursion
847 ;; invoke standard text indentation here 994 (goto-char pos)
848 (`inside-string 995 (current-indentation)))
849 (goto-char context-start) 996 opening-block-start-points))))
850 (current-indentation)) 997 (`(,(or :inside-paren-newline-start-from-block) . ,start)
851 ;; After backslash we have several possibilities. 998 ;; Add two indentation levels to make the suite stand out.
852 (`after-backslash 999 (goto-char start)
853 (cond 1000 (+ (current-indentation) (* python-indent-offset 2)))))))
854 ;; Check if current line is a dot continuation. For this 1001
855 ;; the current line must start with a dot and previous 1002(defun python-indent--calculate-levels (indentation)
856 ;; line must contain a dot too. 1003 "Calculate levels list given INDENTATION.
857 ((save-excursion 1004Argument INDENTATION can either be an integer or a list of
858 (back-to-indentation) 1005integers. Levels are returned in ascending order, and in the
859 (when (looking-at "\\.") 1006case INDENTATION is a list, this order is enforced."
860 ;; If after moving one line back point is inside a paren it 1007 (if (listp indentation)
861 ;; needs to move back until it's not anymore 1008 (sort (copy-sequence indentation) #'<)
862 (while (prog2 1009 (let* ((remainder (% indentation python-indent-offset))
863 (forward-line -1) 1010 (steps (/ (- indentation remainder) python-indent-offset))
864 (and (not (bobp)) 1011 (levels (mapcar (lambda (step)
865 (python-syntax-context 'paren)))) 1012 (* python-indent-offset step))
866 (goto-char (line-end-position)) 1013 (number-sequence steps 0 -1))))
867 (while (and (re-search-backward 1014 (reverse
868 "\\." (line-beginning-position) t) 1015 (if (not (zerop remainder))
869 (python-syntax-context-type))) 1016 (cons indentation levels)
870 (if (and (looking-at "\\.") 1017 levels)))))
871 (not (python-syntax-context-type))) 1018
872 ;; The indentation is the same column of the 1019(defun python-indent--previous-level (levels indentation)
873 ;; first matching dot that's not inside a 1020 "Return previous level from LEVELS relative to INDENTATION."
874 ;; comment, a string or a paren 1021 (let* ((levels (sort (copy-sequence levels) #'>))
875 (current-column) 1022 (default (car levels)))
876 ;; No dot found on previous line, just add another 1023 (catch 'return
877 ;; indentation level. 1024 (dolist (level levels)
878 (+ (current-indentation) python-indent-offset))))) 1025 (when (funcall #'< level indentation)
879 ;; Check if prev line is a block continuation 1026 (throw 'return level)))
880 ((let ((block-continuation-start 1027 default)))
881 (python-info-block-continuation-line-p))) 1028
882 (when block-continuation-start 1029(defun python-indent-calculate-indentation (&optional previous)
883 ;; If block-continuation-start is set jump to that 1030 "Calculate indentation.
884 ;; marker and use first column after the block start 1031Get indentation of PREVIOUS level when argument is non-nil.
885 ;; as indentation value. 1032Return the max level of the cycle when indentation reaches the
886 (goto-char block-continuation-start) 1033minimum."
887 (re-search-forward 1034 (let* ((indentation (python-indent--calculate-indentation))
888 (python-rx block-start (* space)) 1035 (levels (python-indent--calculate-levels indentation)))
889 (line-end-position) t) 1036 (if previous
890 (current-column)))) 1037 (python-indent--previous-level levels (current-indentation))
891 ;; Check if current line is an assignment continuation 1038 (apply #'max levels))))
892 ((let ((assignment-continuation-start 1039
893 (python-info-assignment-continuation-line-p))) 1040(defun python-indent-line (&optional previous)
894 (when assignment-continuation-start
895 ;; If assignment-continuation is set jump to that
896 ;; marker and use first column after the assignment
897 ;; operator as indentation value.
898 (goto-char assignment-continuation-start)
899 (current-column))))
900 (t
901 (forward-line -1)
902 (goto-char (python-info-beginning-of-backslash))
903 (if (save-excursion
904 (and
905 (forward-line -1)
906 (goto-char
907 (or (python-info-beginning-of-backslash) (point)))
908 (python-info-line-ends-backslash-p)))
909 ;; The two previous lines ended in a backslash so we must
910 ;; respect previous line indentation.
911 (current-indentation)
912 ;; What happens here is that we are dealing with the second
913 ;; line of a backslash continuation, in that case we just going
914 ;; to add one indentation level.
915 (+ (current-indentation) python-indent-offset)))))
916 ;; When inside a paren there's a need to handle nesting
917 ;; correctly
918 (`inside-paren
919 (cond
920 ;; If current line closes the outermost open paren use the
921 ;; current indentation of the context-start line.
922 ((save-excursion
923 (skip-syntax-forward "\s" (line-end-position))
924 (when (and (looking-at (regexp-opt '(")" "]" "}")))
925 (progn
926 (forward-char 1)
927 (not (python-syntax-context 'paren))))
928 (goto-char context-start)
929 (current-indentation))))
930 ;; If open paren is contained on a line by itself add another
931 ;; indentation level, else look for the first word after the
932 ;; opening paren and use it's column position as indentation
933 ;; level.
934 ((let* ((content-starts-in-newline)
935 (indent
936 (save-excursion
937 (if (setq content-starts-in-newline
938 (progn
939 (goto-char context-start)
940 (forward-char)
941 (save-restriction
942 (narrow-to-region
943 (line-beginning-position)
944 (line-end-position))
945 (python-util-forward-comment))
946 (looking-at "$")))
947 (+ (current-indentation) python-indent-offset)
948 (current-column)))))
949 ;; Adjustments
950 (cond
951 ;; If current line closes a nested open paren de-indent one
952 ;; level.
953 ((progn
954 (back-to-indentation)
955 (looking-at (regexp-opt '(")" "]" "}"))))
956 (- indent python-indent-offset))
957 ;; If the line of the opening paren that wraps the current
958 ;; line starts a block add another level of indentation to
959 ;; follow new pep8 recommendation. See: http://ur1.ca/5rojx
960 ((save-excursion
961 (when (and content-starts-in-newline
962 (progn
963 (goto-char context-start)
964 (back-to-indentation)
965 (looking-at (python-rx block-start))))
966 (+ indent python-indent-offset))))
967 (t indent)))))))))))
968
969(defun python-indent-calculate-levels ()
970 "Calculate `python-indent-levels' and reset `python-indent-current-level'."
971 (if (or (python-info-continuation-line-p)
972 (not (python-info-dedenter-statement-p)))
973 ;; XXX: This asks for a refactor. Even if point is on a
974 ;; dedenter statement, it could be multiline and in that case
975 ;; the continuation lines should be indented with normal rules.
976 (let* ((indentation (python-indent-calculate-indentation))
977 (remainder (% indentation python-indent-offset))
978 (steps (/ (- indentation remainder) python-indent-offset)))
979 (setq python-indent-levels (list 0))
980 (dotimes (step steps)
981 (push (* python-indent-offset (1+ step)) python-indent-levels))
982 (when (not (eq 0 remainder))
983 (push (+ (* python-indent-offset steps) remainder) python-indent-levels)))
984 (setq python-indent-levels
985 (or
986 (mapcar (lambda (pos)
987 (save-excursion
988 (goto-char pos)
989 (current-indentation)))
990 (python-info-dedenter-opening-block-positions))
991 (list 0))))
992 (setq python-indent-current-level (1- (length python-indent-levels))
993 python-indent-levels (nreverse python-indent-levels)))
994
995(defun python-indent-toggle-levels ()
996 "Toggle `python-indent-current-level' over `python-indent-levels'."
997 (setq python-indent-current-level (1- python-indent-current-level))
998 (when (< python-indent-current-level 0)
999 (setq python-indent-current-level (1- (length python-indent-levels)))))
1000
1001(defun python-indent-line (&optional force-toggle)
1002 "Internal implementation of `python-indent-line-function'. 1041 "Internal implementation of `python-indent-line-function'.
1003Uses the offset calculated in 1042Use the PREVIOUS level when argument is non-nil, otherwise indent
1004`python-indent-calculate-indentation' and available levels 1043to the maxium available level. When indentation is the minimum
1005indicated by the variable `python-indent-levels' to set the 1044possible and PREVIOUS is non-nil, cycle back to the maximum
1006current indentation. 1045level."
1046 (let ((follow-indentation-p
1047 ;; Check if point is within indentation.
1048 (and (<= (line-beginning-position) (point))
1049 (>= (+ (line-beginning-position)
1050 (current-indentation))
1051 (point)))))
1052 (save-excursion
1053 (indent-line-to
1054 (python-indent-calculate-indentation previous))
1055 (python-info-dedenter-opening-block-message))
1056 (when follow-indentation-p
1057 (back-to-indentation))))
1007 1058
1008When the variable `last-command' is equal to one of the symbols 1059(defun python-indent-calculate-levels ()
1009inside `python-indent-trigger-commands' or FORCE-TOGGLE is 1060 "Return possible indentation levels."
1010non-nil it cycles levels indicated in the variable 1061 (python-indent--calculate-levels
1011`python-indent-levels' by setting the current level in the 1062 (python-indent--calculate-indentation)))
1012variable `python-indent-current-level'.
1013
1014When the variable `last-command' is not equal to one of the
1015symbols inside `python-indent-trigger-commands' and FORCE-TOGGLE
1016is nil it calculates possible indentation levels and saves them
1017in the variable `python-indent-levels'. Afterwards it sets the
1018variable `python-indent-current-level' correctly so offset is
1019equal to
1020 (nth python-indent-current-level python-indent-levels)"
1021 (or
1022 (and (or (and (memq this-command python-indent-trigger-commands)
1023 (eq last-command this-command))
1024 force-toggle)
1025 (not (equal python-indent-levels '(0)))
1026 (or (python-indent-toggle-levels) t))
1027 (python-indent-calculate-levels))
1028 (let* ((starting-pos (point-marker))
1029 (indent-ending-position
1030 (+ (line-beginning-position) (current-indentation)))
1031 (follow-indentation-p
1032 (or (bolp)
1033 (and (<= (line-beginning-position) starting-pos)
1034 (>= indent-ending-position starting-pos))))
1035 (next-indent (nth python-indent-current-level python-indent-levels)))
1036 (unless (= next-indent (current-indentation))
1037 (beginning-of-line)
1038 (delete-horizontal-space)
1039 (indent-to next-indent)
1040 (goto-char starting-pos))
1041 (and follow-indentation-p (back-to-indentation)))
1042 (python-info-dedenter-opening-block-message))
1043 1063
1044(defun python-indent-line-function () 1064(defun python-indent-line-function ()
1045 "`indent-line-function' for Python mode. 1065 "`indent-line-function' for Python mode.
1046See `python-indent-line' for details." 1066When the variable `last-command' is equal to one of the symbols
1047 (python-indent-line)) 1067inside `python-indent-trigger-commands' it cycles possible
1068indentation levels from right to left."
1069 (python-indent-line
1070 (and (memq this-command python-indent-trigger-commands)
1071 (eq last-command this-command))))
1048 1072
1049(defun python-indent-dedent-line () 1073(defun python-indent-dedent-line ()
1050 "De-indent current line." 1074 "De-indent current line."
1051 (interactive "*") 1075 (interactive "*")
1052 (when (and (not (python-syntax-comment-or-string-p)) 1076 (when (and (not (bolp))
1053 (<= (point-marker) (save-excursion 1077 (not (python-syntax-comment-or-string-p))
1054 (back-to-indentation) 1078 (= (+ (line-beginning-position)
1055 (point-marker))) 1079 (current-indentation))
1056 (> (current-column) 0)) 1080 (point)))
1057 (python-indent-line t) 1081 (python-indent-line t)
1058 t)) 1082 t))
1059 1083
1060(defun python-indent-dedent-line-backspace (arg) 1084(defun python-indent-dedent-line-backspace (arg)
1061 "De-indent current line. 1085 "De-indent current line.
1062Argument ARG is passed to `backward-delete-char-untabify' when 1086Argument ARG is passed to `backward-delete-char-untabify' when
1063point is not in between the indentation." 1087point is not in between the indentation."
1064 (interactive "*p") 1088 (interactive "*p")
1065 (when (not (python-indent-dedent-line)) 1089 (unless (python-indent-dedent-line)
1066 (backward-delete-char-untabify arg))) 1090 (backward-delete-char-untabify arg)))
1091
1067(put 'python-indent-dedent-line-backspace 'delete-selection 'supersede) 1092(put 'python-indent-dedent-line-backspace 'delete-selection 'supersede)
1068 1093
1069(defun python-indent-region (start end) 1094(defun python-indent-region (start end)