diff options
| author | Fabián Ezequiel Gallina | 2015-01-27 00:17:24 -0300 |
|---|---|---|
| committer | Fabián Ezequiel Gallina | 2015-01-27 00:17:24 -0300 |
| commit | 5485e3e5b28f82b46d139c63b8ab77ed1d7d61c9 (patch) | |
| tree | f914aa2e95c6178c437bbc472708a72e388cbc96 /lisp/progmodes/python.el | |
| parent | 3b23e6a70294a3fee23353bfb8a23c7167d7c4ef (diff) | |
| download | emacs-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.el | 695 |
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. | ||
| 687 | The function `python-indent-calculate-levels' does not use it | ||
| 688 | anymore. If you were defadvising it and or depended on this | ||
| 689 | variable for indentation customizations, refactor your code to | ||
| 690 | work 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. | ||
| 696 | The function `python-indent-calculate-levels' does not use it | ||
| 697 | anymore. If you were defadvising it and or depended on this | ||
| 698 | variable for indentation customizations, refactor your code to | ||
| 699 | work 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. |
| 718 | Context information is returned with a cons with the form: | 741 | Context is returned in a cons with the form (STATUS . START). |
| 719 | (STATUS . START) | 742 | |
| 720 | 743 | STATUS can be one of the following: | |
| 721 | Where status can be any of the following symbols: | 744 | |
| 722 | 745 | keyword | |
| 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. |
| 731 | START 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 | 951 | May return an integer for the maximum possible indentation at |
| 805 | (pcase context-status | 952 | current context or a list of integers. The latter case is only |
| 806 | (`no-indent 0) | 953 | happening for :at-dedenter-block-start context since the |
| 807 | (`after-comment | 954 | possibilities 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 | 1004 | Argument INDENTATION can either be an integer or a list of |
| 858 | (back-to-indentation) | 1005 | integers. Levels are returned in ascending order, and in the |
| 859 | (when (looking-at "\\.") | 1006 | case 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 | 1031 | Get indentation of PREVIOUS level when argument is non-nil. |
| 885 | ;; as indentation value. | 1032 | Return the max level of the cycle when indentation reaches the |
| 886 | (goto-char block-continuation-start) | 1033 | minimum." |
| 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'. |
| 1003 | Uses the offset calculated in | 1042 | Use the PREVIOUS level when argument is non-nil, otherwise indent |
| 1004 | `python-indent-calculate-indentation' and available levels | 1043 | to the maxium available level. When indentation is the minimum |
| 1005 | indicated by the variable `python-indent-levels' to set the | 1044 | possible and PREVIOUS is non-nil, cycle back to the maximum |
| 1006 | current indentation. | 1045 | level." |
| 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 | ||
| 1008 | When the variable `last-command' is equal to one of the symbols | 1059 | (defun python-indent-calculate-levels () |
| 1009 | inside `python-indent-trigger-commands' or FORCE-TOGGLE is | 1060 | "Return possible indentation levels." |
| 1010 | non-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))) |
| 1012 | variable `python-indent-current-level'. | ||
| 1013 | |||
| 1014 | When the variable `last-command' is not equal to one of the | ||
| 1015 | symbols inside `python-indent-trigger-commands' and FORCE-TOGGLE | ||
| 1016 | is nil it calculates possible indentation levels and saves them | ||
| 1017 | in the variable `python-indent-levels'. Afterwards it sets the | ||
| 1018 | variable `python-indent-current-level' correctly so offset is | ||
| 1019 | equal 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. |
| 1046 | See `python-indent-line' for details." | 1066 | When the variable `last-command' is equal to one of the symbols |
| 1047 | (python-indent-line)) | 1067 | inside `python-indent-trigger-commands' it cycles possible |
| 1068 | indentation 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. |
| 1062 | Argument ARG is passed to `backward-delete-char-untabify' when | 1086 | Argument ARG is passed to `backward-delete-char-untabify' when |
| 1063 | point is not in between the indentation." | 1087 | point 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) |