aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2012-05-17 00:03:34 -0300
committerFabián Ezequiel Gallina2012-05-17 00:03:34 -0300
commit0674d3fadbd4c5755598dc2791a179dbbe1d018e (patch)
tree348d356abe1a98f2867988b7996a6b59a2862822 /lisp/progmodes/python.el
parentbbd27e0710f2885448949880e5b3e48c6a7b97a5 (diff)
downloademacs-0674d3fadbd4c5755598dc2791a179dbbe1d018e.tar.gz
emacs-0674d3fadbd4c5755598dc2791a179dbbe1d018e.zip
Enhancements on indentation code and related functions.
This commit includes: * A more robust implementation of `python-indent-calculate-indentation'. * enhancements on `python-indent-context' when dealing with backslashes and blocks. * Many changes, comments and enhancements to `python-indent-calculate-indentation'. Many of them especially focused to match pep8 guidelines, being this one the most important new one: http://mail.python.org/pipermail/python-dev/2011-June/111760.html * Better `python-info-line-ends-backslash-p' that would work as intended on narrowed buffers. * `python-info-continuation-line-p' now does what's supposed to do. * Enhanced implementation of `python-info-continuation-line-p', `python-info-block-continuation-line-p' and `python-info-assignment-continuation-line-p' New Functions: * `python-util-forward-comment' a simple replacement `forward-comment' with some necessary enhancements.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el418
1 files changed, 238 insertions, 180 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 2f886a5b803..3244f299778 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -535,30 +535,35 @@ These make `python-indent-calculate-indentation' subtract the value of
535 (save-restriction 535 (save-restriction
536 (widen) 536 (widen)
537 (goto-char (point-min)) 537 (goto-char (point-min))
538 (let ((found-block)) 538 (let ((block-end))
539 (while (and (not found-block) 539 (while (and (not block-end)
540 (re-search-forward 540 (re-search-forward
541 (python-rx line-start block-start) nil t)) 541 (python-rx line-start block-start) nil t))
542 (when (and (not (python-info-ppss-context 'string)) 542 (when (and
543 (not (python-info-ppss-context 'comment)) 543 (not (python-info-ppss-context-type))
544 (progn 544 (progn
545 (goto-char (line-end-position)) 545 (goto-char (line-end-position))
546 (forward-comment -9999) 546 (python-util-forward-comment -1)
547 (eq ?: (char-before)))) 547 (if (equal (char-before) ?:)
548 (setq found-block t))) 548 t
549 (if (not found-block) 549 (forward-line 1)
550 (when (python-info-block-continuation-line-p)
551 (while (and (python-info-continuation-line-p)
552 (not (eobp)))
553 (forward-line 1))
554 (python-util-forward-comment -1)
555 (when (equal (char-before) ?:)
556 t)))))
557 (setq block-end (point-marker))))
558 (let ((indentation
559 (when block-end
560 (goto-char block-end)
561 (python-util-forward-comment)
562 (current-indentation))))
563 (if indentation
564 (setq python-indent-offset indentation)
550 (message "Can't guess python-indent-offset, using defaults: %s" 565 (message "Can't guess python-indent-offset, using defaults: %s"
551 python-indent-offset) 566 python-indent-offset)))))))
552 (while (and (progn
553 (goto-char (line-end-position))
554 (python-info-continuation-line-p))
555 (not (eobp)))
556 (forward-line 1))
557 (forward-line 1)
558 (forward-comment 9999)
559 (let ((indent-offset (current-indentation)))
560 (when (> indent-offset 0)
561 (setq python-indent-offset indent-offset))))))))
562 567
563(defun python-indent-context () 568(defun python-indent-context ()
564 "Get information on indentation context. 569 "Get information on indentation context.
@@ -594,42 +599,32 @@ START is the buffer position where the sexp starts."
594 ((setq start (when (not (or (python-info-ppss-context 'string ppss) 599 ((setq start (when (not (or (python-info-ppss-context 'string ppss)
595 (python-info-ppss-context 'comment ppss))) 600 (python-info-ppss-context 'comment ppss)))
596 (let ((line-beg-pos (line-beginning-position))) 601 (let ((line-beg-pos (line-beginning-position)))
597 (when (eq ?\\ (char-before (1- line-beg-pos))) 602 (when (python-info-line-ends-backslash-p (1- line-beg-pos))
598 (- line-beg-pos 2))))) 603 (- line-beg-pos 2)))))
599 'after-backslash) 604 'after-backslash)
600 ;; After beginning of block 605 ;; After beginning of block
601 ((setq start (save-excursion 606 ((setq start (save-excursion
602 (let ((block-regexp (python-rx block-start)) 607 (when (progn
603 (block-start-line-end ":[[:space:]]*$")) 608 (back-to-indentation)
604 (back-to-indentation) 609 (python-util-forward-comment -1)
605 (forward-comment -9999) 610 (equal (char-before) ?:))
606 (back-to-indentation) 611 ;; Move to the first block start that's not in within
607 (when (or (python-info-continuation-line-p) 612 ;; a string, comment or paren and that's not a
608 (and (not (looking-at block-regexp)) 613 ;; continuation line.
609 (save-excursion 614 (while (and (re-search-backward
610 (re-search-forward 615 (python-rx block-start) nil t)
611 block-start-line-end 616 (or
612 (line-end-position) t)))) 617 (python-info-ppss-context 'string)
613 (while (and (forward-line -1) 618 (python-info-ppss-context 'comment)
614 (python-info-continuation-line-p) 619 (python-info-ppss-context 'paren)
615 (not (bobp)))) 620 (python-info-continuation-line-p))))
616 (back-to-indentation) 621 (when (looking-at (python-rx block-start))
617 (when (not (looking-at block-regexp))
618 (forward-line 1)))
619 (back-to-indentation)
620 (when (and (looking-at block-regexp)
621 (or (re-search-forward
622 block-start-line-end
623 (line-end-position) t)
624 (save-excursion
625 (goto-char (line-end-position))
626 (python-info-continuation-line-p))))
627 (point-marker))))) 622 (point-marker)))))
628 'after-beginning-of-block) 623 'after-beginning-of-block)
629 ;; After normal line 624 ;; After normal line
630 ((setq start (save-excursion 625 ((setq start (save-excursion
631 (back-to-indentation) 626 (back-to-indentation)
632 (forward-comment -9999) 627 (python-util-forward-comment -1)
633 (python-nav-sentence-start) 628 (python-nav-sentence-start)
634 (point-marker))) 629 (point-marker)))
635 'after-line) 630 'after-line)
@@ -647,9 +642,14 @@ START is the buffer position where the sexp starts."
647 (save-excursion 642 (save-excursion
648 (case context-status 643 (case context-status
649 ('no-indent 0) 644 ('no-indent 0)
645 ;; When point is after beginning of block just add one level
646 ;; of indentation relative to the context-start
650 ('after-beginning-of-block 647 ('after-beginning-of-block
651 (goto-char context-start) 648 (goto-char context-start)
652 (+ (current-indentation) python-indent-offset)) 649 (+ (current-indentation) python-indent-offset))
650 ;; When after a simple line just use previous line
651 ;; indentation, in the case current line starts with a
652 ;; `python-indent-dedenters' de-indent one level.
653 ('after-line 653 ('after-line
654 (- 654 (-
655 (save-excursion 655 (save-excursion
@@ -660,92 +660,125 @@ START is the buffer position where the sexp starts."
660 (looking-at (regexp-opt python-indent-dedenters))) 660 (looking-at (regexp-opt python-indent-dedenters)))
661 python-indent-offset 661 python-indent-offset
662 0))) 662 0)))
663 ;; When inside of a string, do nothing. just use the current
664 ;; indentation. XXX: perhaps it would be a good idea to
665 ;; invoke standard text indentation here
663 ('inside-string 666 ('inside-string
664 (goto-char context-start) 667 (goto-char context-start)
665 (current-indentation)) 668 (current-indentation))
669 ;; After backslash we have several posibilities
666 ('after-backslash 670 ('after-backslash
667 (let* ((block-continuation 671 (cond
668 (save-excursion 672 ;; Check if current line is a dot continuation. For this
669 (forward-line -1) 673 ;; the current line must start with a dot and previous
670 (python-info-block-continuation-line-p))) 674 ;; line must contain a dot too.
671 (assignment-continuation 675 ((save-excursion
672 (save-excursion 676 (back-to-indentation)
673 (forward-line -1) 677 (when (looking-at "\\.")
674 (python-info-assignment-continuation-line-p))) 678 (forward-line -1)
675 (dot-continuation 679 (goto-char (line-end-position))
676 (save-excursion 680 (while (and (re-search-backward "\\." (line-beginning-position) t)
677 (back-to-indentation) 681 (or (python-info-ppss-context 'comment)
678 (when (looking-at "\\.") 682 (python-info-ppss-context 'string)
679 (forward-line -1) 683 (python-info-ppss-context 'paren))))
680 (goto-char (line-end-position)) 684 (if (and (looking-at "\\.")
681 (while (and (re-search-backward "\\." (line-beginning-position) t) 685 (not (or (python-info-ppss-context 'comment)
682 (or (python-info-ppss-context 'comment) 686 (python-info-ppss-context 'string)
683 (python-info-ppss-context 'string) 687 (python-info-ppss-context 'paren))))
684 (python-info-ppss-context 'paren)))) 688 ;; The indentation is the same column of the
685 (if (and (looking-at "\\.") 689 ;; first matching dot that's not inside a
686 (not (or (python-info-ppss-context 'comment) 690 ;; comment, a string or a paren
687 (python-info-ppss-context 'string) 691 (current-column)
688 (python-info-ppss-context 'paren)))) 692 ;; No dot found on previous line, just add another
689 (current-column) 693 ;; indentation level.
690 (+ (current-indentation) python-indent-offset))))) 694 (+ (current-indentation) python-indent-offset)))))
691 (indentation (cond 695 ;; Check if prev line is a block continuation
692 (dot-continuation 696 ((let ((block-continuation-start
693 dot-continuation) 697 (python-info-block-continuation-line-p)))
694 (block-continuation 698 (when block-continuation-start
695 (goto-char block-continuation) 699 ;; If block-continuation-start is set jump to that
696 (re-search-forward 700 ;; marker and use first column after the block start
697 (python-rx block-start (* space)) 701 ;; as indentation value.
698 (line-end-position) t) 702 (goto-char block-continuation-start)
699 (current-column)) 703 (re-search-forward
700 (assignment-continuation 704 (python-rx block-start (* space))
701 (goto-char assignment-continuation) 705 (line-end-position) t)
702 (re-search-forward 706 (current-column))))
703 (python-rx simple-operator) 707 ;; Check if current line is an assignment continuation
704 (line-end-position) t) 708 ((let ((assignment-continuation-start
705 (forward-char 1) 709 (python-info-assignment-continuation-line-p)))
706 (re-search-forward 710 (when assignment-continuation-start
707 (python-rx (* space)) 711 ;; If assignment-continuation is set jump to that
708 (line-end-position) t) 712 ;; marker and use first column after the assignment
709 (current-column)) 713 ;; operator as indentation value.
710 (t 714 (goto-char assignment-continuation-start)
711 (goto-char context-start) 715 (current-column))))
712 (if (not 716 (t
713 (save-excursion 717 (forward-line -1)
714 (back-to-indentation) 718 (if (save-excursion
715 (looking-at 719 (and
716 "\\(?:return\\|from\\|import\\)\s+"))) 720 (python-info-line-ends-backslash-p)
717 (current-indentation) 721 (forward-line -1)
718 (+ (current-indentation) 722 (python-info-line-ends-backslash-p)))
719 (length 723 ;; The two previous lines ended in a backslash so we must
720 (match-string-no-properties 0)))))))) 724 ;; respect previous line indentation.
721 indentation)) 725 (current-indentation)
726 ;; What happens here is that we are dealing with the second
727 ;; line of a backslash continuation, in that case we just going
728 ;; to add one indentation level.
729 (+ (current-indentation) python-indent-offset)))))
730 ;; When inside a paren there's a need to handle nesting
731 ;; correctly
722 ('inside-paren 732 ('inside-paren
723 (or (save-excursion 733 (cond
724 (skip-syntax-forward "\s" (line-end-position)) 734 ;; If current line closes the outtermost open paren use the
725 (when (and (looking-at (regexp-opt '(")" "]" "}"))) 735 ;; current indentation of the context-start line.
726 (not (forward-char 1)) 736 ((save-excursion
727 (not (python-info-ppss-context 'paren))) 737 (skip-syntax-forward "\s" (line-end-position))
728 (goto-char context-start) 738 (when (and (looking-at (regexp-opt '(")" "]" "}")))
739 (progn
740 (forward-char 1)
741 (not (python-info-ppss-context 'paren))))
742 (goto-char context-start)
743 (current-indentation))))
744 ;; If open paren is contained on a line by itself add another
745 ;; indentation level, else look for the first word after the
746 ;; opening paren and use it's column position as indentation
747 ;; level.
748 ((let* ((content-starts-in-newline)
749 (indent
750 (save-excursion
751 (if (setq content-starts-in-newline
752 (progn
753 (goto-char context-start)
754 (forward-char)
755 (save-restriction
756 (narrow-to-region
757 (line-beginning-position)
758 (line-end-position))
759 (python-util-forward-comment))
760 (looking-at "$")))
761 (+ (current-indentation) python-indent-offset)
762 (current-column)))))
763 ;; Adjustments
764 (cond
765 ;; If current line closes a nested open paren de-indent one
766 ;; level.
767 ((progn
729 (back-to-indentation) 768 (back-to-indentation)
730 (current-column))) 769 (looking-at (regexp-opt '(")" "]" "}"))))
731 (- 770 (- indent python-indent-offset))
732 (save-excursion 771 ;; If the line of the opening paren that wraps the current
733 (goto-char context-start) 772 ;; line starts a block add another level of indentation to
734 (forward-char) 773 ;; follow new pep8 recommendation. See: http://ur1.ca/5rojx
735 (save-restriction 774 ((save-excursion
736 (narrow-to-region 775 (when (and content-starts-in-newline
737 (line-beginning-position) 776 (progn
738 (line-end-position)) 777 (goto-char context-start)
739 (forward-comment 9999)) 778 (back-to-indentation)
740 (if (looking-at "$") 779 (looking-at (python-rx block-start))))
741 (+ (current-indentation) python-indent-offset) 780 (+ indent python-indent-offset))))
742 (forward-comment 9999) 781 (t indent)))))))))))
743 (current-column)))
744 (if (progn
745 (back-to-indentation)
746 (looking-at (regexp-opt '(")" "]" "}"))))
747 python-indent-offset
748 0)))))))))
749 782
750(defun python-indent-calculate-levels () 783(defun python-indent-calculate-levels ()
751 "Calculate `python-indent-levels' and reset `python-indent-current-level'." 784 "Calculate `python-indent-levels' and reset `python-indent-current-level'."
@@ -972,7 +1005,7 @@ decorators are not included. Return non-nil if point is moved to the
972 (let ((found)) 1005 (let ((found))
973 (dotimes (i (- arg) found) 1006 (dotimes (i (- arg) found)
974 (python-end-of-defun-function) 1007 (python-end-of-defun-function)
975 (forward-comment 9999) 1008 (python-util-forward-comment)
976 (goto-char (line-end-position)) 1009 (goto-char (line-end-position))
977 (when (not (eobp)) 1010 (when (not (eobp))
978 (setq found 1011 (setq found
@@ -996,7 +1029,7 @@ Returns nil if point is not in a def or class."
996 (not (eobp)) 1029 (not (eobp))
997 (or (not (current-word)) 1030 (or (not (current-word))
998 (> (current-indentation) beg-defun-indent)))) 1031 (> (current-indentation) beg-defun-indent))))
999 (forward-comment 9999) 1032 (python-util-forward-comment)
1000 (goto-char (line-beginning-position)))) 1033 (goto-char (line-beginning-position))))
1001 1034
1002(defun python-nav-sentence-start () 1035(defun python-nav-sentence-start ()
@@ -1036,13 +1069,13 @@ With negative argument, move backward repeatedly to start of sentence."
1036 (interactive "^p") 1069 (interactive "^p")
1037 (or arg (setq arg 1)) 1070 (or arg (setq arg 1))
1038 (while (> arg 0) 1071 (while (> arg 0)
1039 (forward-comment 9999) 1072 (python-util-forward-comment)
1040 (python-nav-sentence-end) 1073 (python-nav-sentence-end)
1041 (forward-line 1) 1074 (forward-line 1)
1042 (setq arg (1- arg))) 1075 (setq arg (1- arg)))
1043 (while (< arg 0) 1076 (while (< arg 0)
1044 (python-nav-sentence-end) 1077 (python-nav-sentence-end)
1045 (forward-comment -9999) 1078 (python-util-forward-comment -1)
1046 (python-nav-sentence-start) 1079 (python-nav-sentence-start)
1047 (forward-line -1) 1080 (forward-line -1)
1048 (setq arg (1+ arg)))) 1081 (setq arg (1+ arg))))
@@ -2415,7 +2448,7 @@ not inside a defun."
2415 (widen) 2448 (widen)
2416 (save-excursion 2449 (save-excursion
2417 (goto-char (line-end-position)) 2450 (goto-char (line-end-position))
2418 (forward-comment -9999) 2451 (python-util-forward-comment -1)
2419 (setq min-indent (current-indentation)) 2452 (setq min-indent (current-indentation))
2420 (while (python-beginning-of-defun-function 1 t) 2453 (while (python-beginning-of-defun-function 1 t)
2421 (when (or (< (current-indentation) min-indent) 2454 (when (or (< (current-indentation) min-indent)
@@ -2462,68 +2495,83 @@ not inside a defun."
2462 (when (member (current-word) '("except" "else")) 2495 (when (member (current-word) '("except" "else"))
2463 (point-marker)))))))) 2496 (point-marker))))))))
2464 2497
2465(defun python-info-line-ends-backslash-p () 2498(defun python-info-line-ends-backslash-p (&optional line-number)
2466 "Return non-nil if current line ends with backslash." 2499 "Return non-nil if current line ends with backslash.
2467 (string= (or (ignore-errors 2500With optional argument LINE-NUMBER, check that line instead."
2468 (buffer-substring 2501 (save-excursion
2469 (line-end-position) 2502 (save-restriction
2470 (- (line-end-position) 1))) "") "\\")) 2503 (when line-number
2504 (goto-char line-number))
2505 (widen)
2506 (goto-char (line-end-position))
2507 (equal (char-after (1- (point))) ?\\))))
2471 2508
2472(defun python-info-continuation-line-p () 2509(defun python-info-continuation-line-p ()
2473 "Return non-nil if current line is continuation of another." 2510 "Check if current line is continuation of another.
2474 (let ((current-ppss-context-type (python-info-ppss-context-type))) 2511When current line is continuation of another return the point
2475 (and 2512where the continued line ends."
2476 (equal (save-excursion 2513 (save-excursion
2477 (goto-char (line-end-position)) 2514 (save-restriction
2478 (forward-comment 9999) 2515 (widen)
2479 (python-info-ppss-context-type)) 2516 (let* ((context-type (progn
2480 current-ppss-context-type) 2517 (back-to-indentation)
2481 (or (python-info-line-ends-backslash-p) 2518 (python-info-ppss-context-type)))
2482 (string-match ",[[:space:]]*$" (buffer-substring 2519 (line-start (line-number-at-pos))
2483 (line-beginning-position) 2520 (context-start (when context-type
2484 (line-end-position))) 2521 (python-info-ppss-context context-type))))
2485 (save-excursion 2522 (cond ((equal context-type 'paren)
2486 (let ((innermost-paren (progn 2523 ;; Lines inside a paren are always a continuation line
2487 (goto-char (line-end-position)) 2524 ;; (except the first one).
2488 (python-info-ppss-context 'paren)))) 2525 (when (equal (python-info-ppss-context-type) 'paren)
2489 (when (and innermost-paren 2526 (python-util-forward-comment -1)
2490 (and (<= (line-beginning-position) innermost-paren) 2527 (python-util-forward-comment -1)
2491 (>= (line-end-position) innermost-paren))) 2528 (point-marker)))
2492 (goto-char innermost-paren) 2529 ((or (equal context-type 'comment)
2493 (looking-at (python-rx open-paren (* space) line-end))))) 2530 (equal context-type 'string))
2494 (save-excursion 2531 ;; move forward an roll again
2495 (back-to-indentation) 2532 (goto-char context-start)
2496 (python-info-ppss-context 'paren)))))) 2533 (python-util-forward-comment)
2534 (python-info-continuation-line-p))
2535 (t
2536 ;; Not within a paren, string or comment, the only way we are
2537 ;; dealing with a continuation line is that previous line
2538 ;; contains a backslash, and this can only be the previous line
2539 ;; from current
2540 (back-to-indentation)
2541 (python-util-forward-comment -1)
2542 (python-util-forward-comment -1)
2543 (when (and (equal (1- line-start) (line-number-at-pos))
2544 (python-info-line-ends-backslash-p))
2545 (point-marker))))))))
2497 2546
2498(defun python-info-block-continuation-line-p () 2547(defun python-info-block-continuation-line-p ()
2499 "Return non-nil if current line is a continuation of a block." 2548 "Return non-nil if current line is a continuation of a block."
2500 (save-excursion 2549 (save-excursion
2501 (while (and (not (bobp)) 2550 (when (python-info-continuation-line-p)
2502 (python-info-continuation-line-p)) 2551 (forward-line -1)
2503 (forward-line -1)) 2552 (back-to-indentation)
2504 (forward-line 1) 2553 (when (looking-at (python-rx block-start))
2505 (back-to-indentation) 2554 (point-marker)))))
2506 (when (looking-at (python-rx block-start))
2507 (point-marker))))
2508 2555
2509(defun python-info-assignment-continuation-line-p () 2556(defun python-info-assignment-continuation-line-p ()
2510 "Return non-nil if current line is a continuation of an assignment." 2557 "Check if current line is a continuation of an assignment.
2558When current line is continuation of another with an assignment
2559return the point of the first non-blank character after the
2560operator."
2511 (save-excursion 2561 (save-excursion
2512 (while (and (not (bobp)) 2562 (when (python-info-continuation-line-p)
2513 (python-info-continuation-line-p)) 2563 (forward-line -1)
2514 (forward-line -1)) 2564 (back-to-indentation)
2515 (forward-line 1) 2565 (when (and (not (looking-at (python-rx block-start)))
2516 (back-to-indentation)
2517 (when (and (not (looking-at (python-rx block-start)))
2518 (save-excursion
2519 (and (re-search-forward (python-rx not-simple-operator 2566 (and (re-search-forward (python-rx not-simple-operator
2520 assignment-operator 2567 assignment-operator
2521 not-simple-operator) 2568 not-simple-operator)
2522 (line-end-position) t) 2569 (line-end-position) t)
2523 (not (or (python-info-ppss-context 'string) 2570 (not (or (python-info-ppss-context 'string)
2524 (python-info-ppss-context 'paren) 2571 (python-info-ppss-context 'paren)
2525 (python-info-ppss-context 'comment)))))) 2572 (python-info-ppss-context 'comment)))))
2526 (point-marker)))) 2573 (skip-syntax-forward "\s")
2574 (point-marker)))))
2527 2575
2528(defun python-info-ppss-context (type &optional syntax-ppss) 2576(defun python-info-ppss-context (type &optional syntax-ppss)
2529 "Return non-nil if point is on TYPE using SYNTAX-PPSS. 2577 "Return non-nil if point is on TYPE using SYNTAX-PPSS.
@@ -2578,6 +2626,16 @@ to \"^python-\"."
2578 (cdr pair)))) 2626 (cdr pair))))
2579 (buffer-local-variables from-buffer))) 2627 (buffer-local-variables from-buffer)))
2580 2628
2629(defun python-util-forward-comment (&optional direction)
2630 "Python mode specific version of `forward-comment'."
2631 (let ((comment-start (python-info-ppss-context 'comment))
2632 (factor (if (< (or direction 0) 0)
2633 -99999
2634 99999)))
2635 (when comment-start
2636 (goto-char comment-start))
2637 (forward-comment factor)))
2638
2581 2639
2582;;;###autoload 2640;;;###autoload
2583(define-derived-mode python-mode fundamental-mode "Python" 2641(define-derived-mode python-mode fundamental-mode "Python"