]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/python.el
* progmodes/python.el: Enhancements to navigation commands.
[gnu-emacs] / lisp / progmodes / python.el
index 71c5453ee2d491548bd8d060b95dfde1f01f1aa0..fe9faf54046ae1f2a0150d20de2515498fd87ff3 100644 (file)
@@ -1,28 +1,28 @@
-;;; python.el --- Python's flying circus support for Emacs
+;;; python.el --- Python's flying circus support for Emacs -*- coding: utf-8 -*-
 
-;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+;; Copyright (C) 2003-2012  Free Software Foundation, Inc.
 
 ;; Author: Fabián E. Gallina <fabian@anue.biz>
 ;; URL: https://github.com/fgallina/python.el
-;; Version: 0.23.1
+;; Version: 0.24.2
 ;; Maintainer: FSF
 ;; Created: Jul 2010
 ;; Keywords: languages
 
-;; This file is NOT part of GNU Emacs.
+;; This file is part of GNU Emacs.
 
-;; python.el is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published
+;; by the Free Software Foundation, either version 3 of the License,
+;; or (at your option) any later version.
 
-;; python.el is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
+;; GNU Emacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with python.el.  If not, see <http://www.gnu.org/licenses/>.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;; indentation bits extracted from original Dave Love's python.el
 ;; found in GNU/Emacs.
 
-;; While it probably has less features than Dave Love's python.el and
-;; PSF's python-mode.el it provides the main stuff you'll need while
-;; keeping it simple :)
-
 ;; Implements Syntax highlighting, Indentation, Movement, Shell
 ;; interaction, Shell completion, Shell virtualenv support, Pdb
 ;; tracking, Symbol completion, Skeletons, FFAP, Code Check, Eldoc,
 
 ;; Movement: `beginning-of-defun' and `end-of-defun' functions are
 ;; properly implemented.  There are also specialized
-;; `forward-sentence' and `backward-sentence' replacements
-;; (`python-nav-forward-sentence', `python-nav-backward-sentence'
-;; respectively).  Extra functions `python-nav-sentence-start' and
-;; `python-nav-sentence-end' are included to move to the beginning and
-;; to the end of a setence while taking care of multiline definitions.
+;; `forward-sentence' and `backward-sentence' replacements called
+;; `python-nav-forward-block', `python-nav-backward-block'
+;; respectively which navigate between beginning of blocks of code.
+;; Extra functions `python-nav-forward-statement',
+;; `python-nav-backward-statement', `python-nav-statement-start',
+;; `python-nav-statement-end', `python-nav-block-start' and
+;; `python-nav-block-end' are included but no bound to any key.
 ;; `python-nav-jump-to-defun' is provided and allows jumping to a
 ;; function or class definition quickly in the current buffer.
 
 ;;        "VIRTUAL_ENV=/path/to/env/"))
 ;; (python-shell-exec-path . ("/path/to/env/bin/"))
 
-;; Since the above is cumbersome and can be programatically
+;; Since the above is cumbersome and can be programmatically
 ;; calculated, the variable `python-shell-virtualenv-path' is
 ;; provided.  When this variable is set with the path of the
 ;; virtualenv to use, `process-environment' and `exec-path' get proper
   (let ((map (make-sparse-keymap)))
     ;; Movement
     (substitute-key-definition 'backward-sentence
-                               'python-nav-backward-sentence
+                               'python-nav-backward-block
                                map global-map)
     (substitute-key-definition 'forward-sentence
-                               'python-nav-forward-sentence
+                               'python-nav-forward-block
                                map global-map)
     (define-key map "\C-c\C-j" 'python-nav-jump-to-defun)
     ;; Indent specific
 
 (eval-when-compile
   (defconst python-rx-constituents
-    (list
-     `(block-start          . ,(rx symbol-start
+    `((block-start          . ,(rx symbol-start
                                    (or "def" "class" "if" "elif" "else" "try"
                                        "except" "finally" "for" "while" "with")
                                    symbol-end))
-     `(decorator            . ,(rx line-start (* space) ?@ (any letter ?_)
+      (decorator            . ,(rx line-start (* space) ?@ (any letter ?_)
                                    (* (any word ?_))))
-     `(defun                . ,(rx symbol-start (or "def" "class") symbol-end))
-     `(if-name-main         . ,(rx line-start "if" (+ space) "__name__"
+      (defun                . ,(rx symbol-start (or "def" "class") symbol-end))
+      (if-name-main         . ,(rx line-start "if" (+ space) "__name__"
                                    (+ space) "==" (+ space)
                                    (any ?' ?\") "__main__" (any ?' ?\")
                                    (* space) ?:))
-     `(symbol-name          . ,(rx (any letter ?_) (* (any word ?_))))
-     `(open-paren           . ,(rx (or "{" "[" "(")))
-     `(close-paren          . ,(rx (or "}" "]" ")")))
-     `(simple-operator      . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))
-     `(not-simple-operator  . ,(rx
+      (symbol-name          . ,(rx (any letter ?_) (* (any word ?_))))
+      (open-paren           . ,(rx (or "{" "[" "(")))
+      (close-paren          . ,(rx (or "}" "]" ")")))
+      (simple-operator      . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))
+      ;; FIXME: rx should support (not simple-operator).
+      (not-simple-operator  . ,(rx
                                 (not
                                  (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))))
-     `(operator             . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">"
+      ;; FIXME: Use regexp-opt.
+      (operator             . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">"
                                        "=" "%" "**" "//" "<<" ">>" "<=" "!="
                                        "==" ">=" "is" "not")))
-     `(assignment-operator  . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**="
+      ;; FIXME: Use regexp-opt.
+      (assignment-operator  . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**="
                                        ">>=" "<<=" "&=" "^=" "|="))))
     "Additional Python specific sexps for `python-rx'"))
 
@@ -430,7 +430,7 @@ This variant of `rx' supports common python named REGEXPS."
            ;; Extra:
            "__all__" "__doc__" "__name__" "__package__")
           symbol-end) . font-lock-builtin-face)
-    ;; asignations
+    ;; assignments
     ;; support for a = b = c = 5
     (,(lambda (limit)
         (let ((re (python-rx (group (+ (any word ?. ?_)))
@@ -460,14 +460,15 @@ This variant of `rx' supports common python named REGEXPS."
               (set-match-data nil)))))
      (1 font-lock-variable-name-face nil nil))))
 
-(defconst python-font-lock-syntactic-keywords
+(defconst python-syntax-propertize-function
   ;; Make outer chars of matching triple-quote sequences into generic
   ;; string delimiters.  Fixme: Is there a better way?
   ;; First avoid a sequence preceded by an odd number of backslashes.
-  `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix.
-              "\\(?:''''''\\|\"\"\"\"\"\"\\)" ; Empty triple-quote
-              "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)")
-     (3 (python-quote-syntax)))))
+  (syntax-propertize-rules
+   (;; ¡Backrefs don't work in syntax-propertize-rules!
+    (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix.
+            "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)")
+    (3 (ignore (python-quote-syntax))))))
 
 (defun python-quote-syntax ()
   "Put `syntax-table' property correctly on triple quote.
@@ -550,6 +551,12 @@ It makes underscores and dots word constituent chars.")
   :group 'python
   :safe 'booleanp)
 
+(define-obsolete-variable-alias
+  'python-indent 'python-indent-offset "24.2")
+
+(define-obsolete-variable-alias
+  'python-guess-indent 'python-indent-guess-indent-offset "24.2")
+
 (defvar python-indent-current-level 0
   "Current indentation level `python-indent-line-function' is using.")
 
@@ -563,6 +570,7 @@ These make `python-indent-calculate-indentation' subtract the value of
 
 (defun python-indent-guess-indent-offset ()
   "Guess and set `python-indent-offset' for the current buffer."
+  (interactive)
   (save-excursion
     (save-restriction
       (widen)
@@ -658,7 +666,7 @@ START is the buffer position where the sexp starts."
         ((setq start (save-excursion
                        (back-to-indentation)
                        (python-util-forward-comment -1)
-                       (python-nav-sentence-start)
+                       (python-nav-statement-start)
                        (point-marker)))
          'after-line)
         ;; Do not indent
@@ -699,7 +707,7 @@ START is the buffer position where the sexp starts."
           ('inside-string
            (goto-char context-start)
            (current-indentation))
-          ;; After backslash we have several posibilities
+          ;; After backslash we have several possibilities.
           ('after-backslash
            (cond
             ;; Check if current line is a dot continuation.  For this
@@ -754,12 +762,12 @@ START is the buffer position where the sexp starts."
                  (current-column))))
             (t
              (forward-line -1)
-             (goto-char (python-info-beginning-of-backlash))
+             (goto-char (python-info-beginning-of-backslash))
              (if (save-excursion
                    (and
                     (forward-line -1)
                     (goto-char
-                     (or (python-info-beginning-of-backlash) (point)))
+                     (or (python-info-beginning-of-backslash) (point)))
                     (python-info-line-ends-backslash-p)))
                  ;; The two previous lines ended in a backslash so we must
                  ;; respect previous line indentation.
@@ -772,7 +780,7 @@ START is the buffer position where the sexp starts."
           ;; correctly
           ('inside-paren
            (cond
-            ;; If current line closes the outtermost open paren use the
+            ;; If current line closes the outermost open paren use the
             ;; current indentation of the context-start line.
             ((save-excursion
                (skip-syntax-forward "\s" (line-end-position))
@@ -1010,86 +1018,91 @@ automatically if needed."
 The name of the defun should be grouped so it can be retrieved
 via `match-string'.")
 
-(defun python-nav-beginning-of-defun (&optional nodecorators)
+(defun python-nav-beginning-of-defun (&optional arg)
   "Move point to `beginning-of-defun'.
-When NODECORATORS is non-nil decorators are not included.  This
-is the main part of`python-beginning-of-defun-function'
-implementation.  Return non-nil if point is moved to the
-`beginning-of-defun'."
-  (let ((indent-pos (save-excursion
-                      (back-to-indentation)
-                      (point-marker)))
-        (found)
-        (include-decorators
-         (lambda ()
-           (when (not nodecorators)
-             (when (save-excursion
-                     (forward-line -1)
-                     (looking-at (python-rx decorator)))
-               (while (and (not (bobp))
-                           (forward-line -1)
-                           (looking-at (python-rx decorator))))
-               (when (not (bobp)) (forward-line 1)))))))
-    (if (and (> (point) indent-pos)
-             (save-excursion
-               (goto-char (line-beginning-position))
-               (looking-at python-nav-beginning-of-defun-regexp)))
-        (progn
-          (goto-char (line-beginning-position))
-          (funcall include-decorators)
-          (setq found t))
-      (goto-char (line-beginning-position))
-      (when (re-search-backward python-nav-beginning-of-defun-regexp nil t)
-        (setq found t))
-      (goto-char (or (python-info-ppss-context 'string) (point)))
-      (funcall include-decorators))
-    found))
-
-(defun python-beginning-of-defun-function (&optional arg nodecorators)
+With positive ARG move search backwards.  With negative do the
+same but forward.  When ARG is nil or 0 defaults to 1.  This is
+the main part of `python-beginning-of-defun-function'.  Return
+non-nil if point is moved to `beginning-of-defun'."
+  (when (or (null arg) (= arg 0)) (setq arg 1))
+  (let* ((re-search-fn (if (> arg 0)
+                           #'re-search-backward
+                         #'re-search-forward))
+         (line-beg-pos (line-beginning-position))
+         (line-content-start (+ line-beg-pos (current-indentation)))
+         (pos (point-marker))
+         (found
+          (progn
+            (when (and (< arg 0)
+                       (python-info-looking-at-beginning-of-defun))
+              (end-of-line 1))
+            (while (and (funcall re-search-fn
+                                 python-nav-beginning-of-defun-regexp nil t)
+                        (python-info-ppss-context-type)))
+            (and (python-info-looking-at-beginning-of-defun)
+                 (or (not (= (line-number-at-pos pos)
+                             (line-number-at-pos)))
+                     (and (>= (point) line-beg-pos)
+                          (<= (point) line-content-start)
+                          (> pos line-content-start)))))))
+    (if found
+        (or (beginning-of-line 1) t)
+      (and (goto-char pos) nil))))
+
+(defun python-beginning-of-defun-function (&optional arg)
   "Move point to the beginning of def or class.
-With positive ARG move that number of functions forward.  With
-negative do the same but backwards.  When NODECORATORS is non-nil
-decorators are not included.  Return non-nil if point is moved to the
-`beginning-of-defun'."
+With positive ARG move that number of functions backwards.  With
+negative do the same but forward.  When ARG is nil or 0 defaults
+to 1.  Return non-nil if point is moved to `beginning-of-defun'."
   (when (or (null arg) (= arg 0)) (setq arg 1))
-  (if (> arg 0)
-      (dotimes (i arg (python-nav-beginning-of-defun nodecorators)))
-    (let ((found))
-      (dotimes (i (- arg) found)
-        (python-end-of-defun-function)
-        (python-util-forward-comment)
-        (goto-char (line-end-position))
-        (when (not (eobp))
-          (setq found
-                (python-nav-beginning-of-defun nodecorators)))))))
+  (let ((found))
+    (cond ((and (eq this-command 'mark-defun)
+                (python-info-looking-at-beginning-of-defun)))
+          (t
+           (dotimes (i (if (> arg 0) arg (- arg)))
+             (when (and (python-nav-beginning-of-defun arg)
+                        (not found))
+               (setq found t)))))
+    found))
 
 (defun python-end-of-defun-function ()
   "Move point to the end of def or class.
 Returns nil if point is not in a def or class."
   (interactive)
-  (let ((beg-defun-indent)
-        (decorator-regexp "[[:space:]]*@"))
-    (when (looking-at decorator-regexp)
-      (while (and (not (eobp))
-                  (forward-line 1)
-                  (looking-at decorator-regexp))))
-    (when (not (looking-at python-nav-beginning-of-defun-regexp))
-      (python-beginning-of-defun-function))
-    (setq beg-defun-indent (current-indentation))
-    (forward-line 1)
-    (while (and (forward-line 1)
-                (not (eobp))
-                (or (not (current-word))
-                    (equal (char-after (+ (point) (current-indentation))) ?#)
-                    (> (current-indentation) beg-defun-indent)
-                    (not (looking-at python-nav-beginning-of-defun-regexp)))))
-    (python-util-forward-comment)
-    (goto-char (line-beginning-position))))
-
-(defun python-nav-sentence-start ()
-  "Move to start of current sentence."
+  (let ((beg-defun-indent))
+    (when (or (python-info-looking-at-beginning-of-defun)
+              (python-beginning-of-defun-function 1)
+              (python-beginning-of-defun-function -1))
+      (setq beg-defun-indent (current-indentation))
+      (forward-line 1)
+      ;; Go as forward as possible
+      (while (and (or
+                   (python-nav-beginning-of-defun -1)
+                   (and (goto-char (point-max)) nil))
+                  (> (current-indentation) beg-defun-indent)))
+      (beginning-of-line 1)
+      ;; Go as backwards as possible
+      (while (and (forward-line -1)
+                  (not (bobp))
+                  (or (not (current-word))
+                      (equal (char-after (+ (point) (current-indentation))) ?#)
+                      (<= (current-indentation) beg-defun-indent)
+                      (looking-at (python-rx decorator))
+                      (python-info-ppss-context-type))))
+      (forward-line 1)
+      ;; If point falls inside a paren or string context the point is
+      ;; forwarded at the end of it (or end of buffer if its not closed)
+      (let ((context-type (python-info-ppss-context-type)))
+        (when (memq context-type '(paren string))
+          ;; Slow but safe.
+          (while (and (not (eobp))
+                      (python-info-ppss-context-type))
+            (forward-line 1)))))))
+
+(defun python-nav-statement-start ()
+  "Move to start of current statement."
   (interactive "^")
-  (while (and (not (back-to-indentation))
+  (while (and (or (back-to-indentation) t)
               (not (bobp))
               (when (or
                      (save-excursion
@@ -1099,8 +1112,8 @@ Returns nil if point is not in a def or class."
                      (python-info-ppss-context 'paren))
                 (forward-line -1)))))
 
-(defun python-nav-sentence-end ()
-  "Move to end of current sentence."
+(defun python-nav-statement-end ()
+  "Move to end of current statement."
   (interactive "^")
   (while (and (goto-char (line-end-position))
               (not (eobp))
@@ -1110,54 +1123,220 @@ Returns nil if point is not in a def or class."
                      (python-info-ppss-context 'paren))
                 (forward-line 1)))))
 
-(defun python-nav-backward-sentence (&optional arg)
-  "Move backward to start of sentence.  With ARG, do it arg times.
-See `python-nav-forward-sentence' for more information."
+(defun python-nav-backward-statement (&optional arg)
+  "Move backward to previous statement.
+With ARG, repeat.  See `python-nav-forward-statement'."
   (interactive "^p")
   (or arg (setq arg 1))
-  (python-nav-forward-sentence (- arg)))
+  (python-nav-forward-statement (- arg)))
 
-(defun python-nav-forward-sentence (&optional arg)
-  "Move forward to next end of sentence.  With ARG, repeat.
-With negative argument, move backward repeatedly to start of sentence."
+(defun python-nav-forward-statement (&optional arg)
+  "Move forward to next statement.
+With ARG, repeat.  With negative argument, move ARG times
+backward to previous statement."
   (interactive "^p")
   (or arg (setq arg 1))
   (while (> arg 0)
+    (python-nav-statement-end)
     (python-util-forward-comment)
-    (python-nav-sentence-end)
-    (forward-line 1)
+    (python-nav-statement-start)
     (setq arg (1- arg)))
   (while (< arg 0)
-    (python-nav-sentence-end)
+    (python-nav-statement-start)
     (python-util-forward-comment -1)
-    (python-nav-sentence-start)
-    (forward-line -1)
+    (python-nav-statement-start)
     (setq arg (1+ arg))))
 
-(defun python-nav-list-defun-positions (&optional include-type)
+(defun python-nav-block-start ()
+  "Move to start of current block."
+  (interactive "^")
+  (let ((starting-pos (point))
+        (block-regexp (python-rx
+                       line-start (* whitespace) block-start)))
+    (if (progn
+          (python-nav-statement-start)
+          (looking-at (python-rx block-start)))
+        (point-marker)
+      ;; Go to first line beginning a statement
+      (while (and (not (bobp))
+                  (or (and (python-nav-statement-start) nil)
+                      (python-info-current-line-comment-p)
+                      (python-info-current-line-empty-p)))
+        (forward-line -1))
+      (let ((block-matching-indent
+             (- (current-indentation) python-indent-offset)))
+        (while
+            (and (python-nav-backward-block)
+                 (> (current-indentation) block-matching-indent)))
+        (if (and (looking-at (python-rx block-start))
+                 (= (current-indentation) block-matching-indent))
+            (point-marker)
+          (and (goto-char starting-pos) nil))))))
+
+(defun python-nav-block-end ()
+  "Move to end of current block."
+  (interactive "^")
+  (when (python-nav-block-start)
+    (let ((block-indentation (current-indentation)))
+      (python-nav-statement-end)
+      (while (and (forward-line 1)
+                  (not (eobp))
+                  (or (and (> (current-indentation) block-indentation)
+                           (or (python-nav-statement-end) t))
+                      (python-info-current-line-comment-p)
+                      (python-info-current-line-empty-p))))
+      (python-util-forward-comment -1)
+      (point-marker))))
+
+(defun python-nav-backward-block (&optional arg)
+  "Move backward to previous block of code.
+With ARG, repeat.  See `python-nav-forward-block'."
+  (interactive "^p")
+  (or arg (setq arg 1))
+  (python-nav-forward-block (- arg)))
+
+(defun python-nav-forward-block (&optional arg)
+  "Move forward to next block of code.
+With ARG, repeat.  With negative argument, move ARG times
+backward to previous block."
+  (interactive "^p")
+  (or arg (setq arg 1))
+  (let ((block-start-regexp
+         (python-rx line-start (* whitespace) block-start))
+        (starting-pos (point)))
+    (while (> arg 0)
+      (python-nav-statement-end)
+      (while (and
+              (re-search-forward block-start-regexp nil t)
+              (or (python-info-ppss-context 'string)
+                  (python-info-ppss-context 'comment)
+                  (python-info-ppss-context 'paren))))
+      (setq arg (1- arg)))
+    (while (< arg 0)
+      (python-nav-statement-start)
+      (while (and
+              (re-search-backward block-start-regexp nil t)
+              (or (python-info-ppss-context 'string)
+                  (python-info-ppss-context 'comment)
+                  (python-info-ppss-context 'paren))))
+      (setq arg (1+ arg)))
+    (python-nav-statement-start)
+    (if (not (looking-at (python-rx block-start)))
+        (and (goto-char starting-pos) nil)
+      (and (not (= (point) starting-pos)) (point-marker)))))
+
+(defun python-nav-forward-sexp-function (&optional arg)
+  "Move forward across one block of code.
+With ARG, do it that many times.  Negative arg -N means
+move backward N times."
+  (interactive "^p")
+  (or arg (setq arg 1))
+  (while (> arg 0)
+    (let ((block-starting-pos
+           (save-excursion (python-nav-block-start)))
+          (block-ending-pos
+           (save-excursion (python-nav-block-end)))
+          (next-block-starting-pos
+           (save-excursion (python-nav-forward-block))))
+      (cond ((not block-starting-pos)
+             (python-nav-forward-block))
+            ((= (point) block-starting-pos)
+             (if (or (not next-block-starting-pos)
+                     (< block-ending-pos next-block-starting-pos))
+                 (python-nav-block-end)
+               (python-nav-forward-block)))
+            ((= block-ending-pos (point))
+             (let ((parent-block-end-pos
+                    (save-excursion
+                      (python-util-forward-comment)
+                      (python-nav-block-start)
+                      (python-nav-block-end))))
+               (if (and parent-block-end-pos
+                        (or (not next-block-starting-pos)
+                            (> next-block-starting-pos parent-block-end-pos)))
+                   (goto-char parent-block-end-pos)
+                 (python-nav-forward-block))))
+            (t (python-nav-block-end))))
+      (setq arg (1- arg)))
+  (while (< arg 0)
+    (let* ((block-starting-pos
+            (save-excursion (python-nav-block-start)))
+           (block-ending-pos
+            (save-excursion (python-nav-block-end)))
+           (prev-block-ending-pos
+            (save-excursion (when (python-nav-backward-block)
+                              (python-nav-block-end))))
+           (prev-block-parent-ending-pos
+            (save-excursion
+              (when prev-block-ending-pos
+                (goto-char prev-block-ending-pos)
+                (python-util-forward-comment)
+                (python-nav-block-start)
+                (python-nav-block-end)))))
+      (cond ((not block-ending-pos)
+             (and (python-nav-backward-block)
+                  (python-nav-block-end)))
+            ((= (point) block-ending-pos)
+             (let ((candidates))
+               (dolist (name
+                        '(prev-block-parent-ending-pos
+                          prev-block-ending-pos
+                          block-ending-pos
+                          block-starting-pos))
+                 (when (and (symbol-value name)
+                            (< (symbol-value name) (point)))
+                   (add-to-list 'candidates (symbol-value name))))
+               (goto-char (apply 'max candidates))))
+            ((> (point) block-ending-pos)
+             (python-nav-block-end))
+            ((= (point) block-starting-pos)
+             (if (not (> (point) (or prev-block-ending-pos (point))))
+                 (python-nav-backward-block)
+               (goto-char prev-block-ending-pos)
+               (let ((parent-block-ending-pos
+                      (save-excursion
+                        (python-nav-forward-sexp-function)
+                        (and (not (looking-at (python-rx block-start)))
+                             (point)))))
+                 (when (and parent-block-ending-pos
+                            (> parent-block-ending-pos prev-block-ending-pos))
+                   (goto-char parent-block-ending-pos)))))
+            (t (python-nav-block-start))))
+    (setq arg (1+ arg))))
+
+(defvar python-nav-list-defun-positions-cache nil)
+(make-variable-buffer-local 'python-nav-list-defun-positions-cache)
+
+(defun python-nav-list-defun-positions (&optional include-type rescan)
   "Make an Alist of defun names and point markers for current buffer.
 When optional argument INCLUDE-TYPE is non-nil the type is
-included the defun name."
-  (let ((defs))
-    (save-restriction
-      (widen)
-      (save-excursion
-        (goto-char (point-max))
-        (while (re-search-backward python-nav-beginning-of-defun-regexp nil t)
-          (when (and (not (python-info-ppss-context 'string))
-                     (not (python-info-ppss-context 'comment))
-                     (not (python-info-ppss-context 'parent)))
-            (add-to-list
-             'defs (cons
-                    (python-info-current-defun include-type)
-                    (point-marker)))))
-        defs))))
-
-(defun python-nav-read-defun ()
+included the defun name.  With optional argument RESCAN the
+`python-nav-list-defun-positions-cache' is invalidated and the
+list of defun is regenerated again."
+  (if (and python-nav-list-defun-positions-cache (not rescan))
+      python-nav-list-defun-positions-cache
+    (let ((defs))
+      (save-restriction
+        (widen)
+        (save-excursion
+          (goto-char (point-max))
+          (while (re-search-backward python-nav-beginning-of-defun-regexp nil t)
+            (when (and (not (python-info-ppss-context 'string))
+                       (not (python-info-ppss-context 'comment))
+                       (not (python-info-ppss-context 'parent)))
+              (add-to-list
+               'defs (cons
+                      (python-info-current-defun include-type)
+                      (point-marker)))))
+          (setq python-nav-list-defun-positions-cache defs))))))
+
+(defun python-nav-read-defun (&optional rescan)
   "Read a defun name of current buffer and return its point marker.
 A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned
-when defun is completed, else nil."
-  (let ((defs (python-nav-list-defun-positions)))
+when defun is completed, else nil.  With optional argument RESCAN
+forces `python-nav-list-defun-positions' to invalidate its
+cache."
+  (let ((defs (python-nav-list-defun-positions nil rescan)))
     (minibuffer-with-setup-hook
         (lambda ()
           (setq minibuffer-completion-table (mapcar 'car defs)))
@@ -1169,9 +1348,11 @@ when defun is completed, else nil."
           (assoc-string stringdef defs))))))
 
 (defun python-nav-jump-to-defun (def)
-  "Jump to the definition of DEF in current file."
+  "Jump to the definition of DEF in current file.
+Locations are cached; use a `C-u' prefix argument to force a
+rescan."
   (interactive
-   (list (python-nav-read-defun)))
+   (list (python-nav-read-defun current-prefix-arg)))
   (when (not (called-interactively-p 'interactive))
     (setq def (assoc-string def (python-nav-list-defun-positions))))
   (let ((def-marker (cdr def)))
@@ -1241,7 +1422,7 @@ Restart the python shell after changing this variable for it to take effect."
 
 (defcustom python-shell-send-setup-max-wait 5
   "Seconds to wait for process output before code setup.
-If output is received before the especified time then control is
+If output is received before the specified time then control is
 returned in that moment and not after waiting."
   :type 'integer
   :group 'python
@@ -1433,11 +1614,10 @@ variable.
   (define-key inferior-python-mode-map (kbd "<tab>")
     'python-shell-completion-complete-or-indent)
   (when python-shell-enable-font-lock
-    (set
-     (make-local-variable 'font-lock-defaults)
-     '(python-font-lock-keywords
-       nil nil nil nil
-       (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords))))
+    (set (make-local-variable 'font-lock-defaults)
+         '(python-font-lock-keywords nil nil nil nil))
+    (set (make-local-variable 'syntax-propertize-function)
+         python-syntax-propertize-function))
   (compilation-shell-minor-mode 1))
 
 (defun python-shell-make-comint (cmd proc-name &optional pop)
@@ -1550,10 +1730,10 @@ there for compatibility with CEDET.")
     (get-buffer-process proc-buffer-name)))
 
 (define-obsolete-function-alias
-  'python-proc 'python-shell-internal-get-or-create-process "23.3")
+  'python-proc 'python-shell-internal-get-or-create-process "24.2")
 
 (define-obsolete-variable-alias
-  'python-buffer 'python-shell-internal-buffer "23.3")
+  'python-buffer 'python-shell-internal-buffer "24.2")
 
 (defun python-shell-send-string (string &optional process msg)
   "Send STRING to inferior Python PROCESS.
@@ -1608,10 +1788,10 @@ Returns the output.  See `python-shell-send-string-no-output'."
    (python-shell-internal-get-or-create-process) nil))
 
 (define-obsolete-function-alias
-  'python-send-receive 'python-shell-internal-send-string "23.3")
+  'python-send-receive 'python-shell-internal-send-string "24.2")
 
 (define-obsolete-function-alias
-  'python-send-string 'python-shell-internal-send-string "23.3")
+  'python-send-string 'python-shell-internal-send-string "24.2")
 
 (defun python-shell-send-region (start end)
   "Send the region delimited by START and END to inferior Python process."
@@ -1621,7 +1801,7 @@ Returns the output.  See `python-shell-send-string-no-output'."
 (defun python-shell-send-buffer (&optional arg)
   "Send the entire buffer to inferior Python process.
 
-With prefix arg include lines protected by \"if __name__ == '__main__':\""
+With prefix ARG include lines surrounded by \"if __name__ == '__main__':\""
   (interactive "P")
   (save-restriction
     (widen)
@@ -1636,17 +1816,23 @@ With prefix arg include lines protected by \"if __name__ == '__main__':\""
 
 (defun python-shell-send-defun (arg)
   "Send the current defun to inferior Python process.
-When argument ARG is non-nil sends the innermost defun."
+When argument ARG is non-nil do not include decorators."
   (interactive "P")
   (save-excursion
     (python-shell-send-region
      (progn
-       (or (python-beginning-of-defun-function)
-           (beginning-of-line))
+       (end-of-line 1)
+       (while (and (or (python-beginning-of-defun-function)
+                       (beginning-of-line 1))
+                   (> (current-indentation) 0)))
+       (when (not arg)
+         (while (and (forward-line -1)
+                     (looking-at (python-rx decorator))))
+         (forward-line 1))
        (point-marker))
      (progn
        (or (python-end-of-defun-function)
-           (end-of-line))
+           (end-of-line 1))
        (point-marker)))))
 
 (defun python-shell-send-file (file-name &optional process temp-file-name)
@@ -1786,7 +1972,7 @@ completions on the current context."
                (overlay-start comint-last-prompt-overlay)
                (overlay-end comint-last-prompt-overlay))))
            (completion-context
-            ;; Check wether a prompt matches a pdb string, an import statement
+            ;; Check whether a prompt matches a pdb string, an import statement
             ;; or just the standard prompt and use the correct
             ;; python-shell-completion-*-code string
             (cond ((and (> (length python-shell-completion-pdb-string-code) 0)
@@ -1939,7 +2125,7 @@ inferior python process is updated properly."
   (interactive)
   (let ((process (python-shell-get-process)))
     (if (not process)
-        (error "Completion needs an inferior Python process running.")
+        (error "Completion needs an inferior Python process running")
       (python-shell-completion--do-completion-at-point process))))
 
 (add-to-list 'debug-ignored-errors
@@ -2099,6 +2285,9 @@ the if condition."
   :group 'python
   :safe 'booleanp)
 
+(define-obsolete-variable-alias
+  'python-use-skeletons 'python-skeleton-autoinsert "24.2")
+
 (defvar python-skeleton-available '()
   "Internal list of available skeletons.")
 
@@ -2118,21 +2307,23 @@ the if condition."
   "Define a `python-mode' skeleton using NAME DOC and SKEL.
 The skeleton will be bound to python-skeleton-NAME and will
 be added to `python-mode-abbrev-table'."
+  (declare (indent 2))
   (let* ((name (symbol-name name))
          (function-name (intern (concat "python-skeleton-" name))))
     `(progn
-       (define-abbrev python-mode-abbrev-table ,name "" ',function-name)
+       (define-abbrev python-mode-abbrev-table ,name "" ',function-name
+        :system t)
        (setq python-skeleton-available
              (cons ',function-name python-skeleton-available))
        (define-skeleton ,function-name
          ,(or doc
               (format "Insert %s statement." name))
          ,@skel))))
-(put 'python-skeleton-define 'lisp-indent-function 2)
 
 (defmacro python-define-auxiliary-skeleton (name doc &optional &rest skel)
   "Define a `python-mode' auxiliary skeleton using NAME DOC and SKEL.
 The skeleton will be bound to python-skeleton-NAME."
+  (declare (indent 2))
   (let* ((name (symbol-name name))
          (function-name (intern (concat "python-skeleton--" name)))
          (msg (format
@@ -2148,7 +2339,6 @@ The skeleton will be bound to python-skeleton-NAME."
        (unless (y-or-n-p ,msg)
          (signal 'quit t))
        ,@skel)))
-(put 'python-define-auxiliary-skeleton 'lisp-indent-function 2)
 
 (python-define-auxiliary-skeleton else nil)
 
@@ -2446,7 +2636,7 @@ It can contain a \"%s\" which will be replaced with the root name."
 
 (defun python-imenu-make-element-tree (element-list full-element plain-index)
   "Make a tree from plain alist of module names.
-ELEMENT-LIST is the defun name splitted by \".\" and FULL-ELEMENT
+ELEMENT-LIST is the defun name split by \".\" and FULL-ELEMENT
 is the same thing, the difference is that FULL-ELEMENT remains
 untouched in all recursive calls.
 Argument PLAIN-INDEX is the calculated plain index used to build the tree."
@@ -2469,7 +2659,7 @@ Argument PLAIN-INDEX is the calculated plain index used to build the tree."
               (push (cons subelement-name subelement-point)
                     python-imenu-index-alist)
             (when (not (listp (cdr path-ref)))
-              ;; Modifiy root cdr to be a list
+              ;; Modify root cdr to be a list.
               (setcdr path-ref
                       (list (cons (format python-imenu-subtree-root-label
                                           (car path-ref))
@@ -2539,10 +2729,9 @@ not inside a defun."
     (save-restriction
       (widen)
       (save-excursion
-        (goto-char (line-end-position))
-        (python-util-forward-comment -1)
+        (end-of-line 1)
         (setq min-indent (current-indentation))
-        (while (python-beginning-of-defun-function 1 t)
+        (while (python-beginning-of-defun-function 1)
           (when (or (< (current-indentation) min-indent)
                     first-run)
             (setq first-run nil)
@@ -2617,8 +2806,9 @@ With optional argument LINE-NUMBER, check that line instead."
       (when (equal (char-before) ?\\)
         (point-marker)))))
 
-(defun python-info-beginning-of-backlash (&optional line-number)
-  "Return the point where the backlashed line starts."
+(defun python-info-beginning-of-backslash (&optional line-number)
+  "Return the point where the backslashed line start.
+Optional argument LINE-NUMBER forces the line number to check against."
   (save-excursion
     (save-restriction
       (widen)
@@ -2728,6 +2918,27 @@ The type returned can be 'comment, 'string or 'paren."
       'paren)
      (t nil))))
 
+(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss)
+  "Check if point is at `beginning-of-defun' using SYNTAX-PPSS."
+  (and (not (python-info-ppss-context-type (or syntax-ppss (syntax-ppss))))
+       (save-excursion
+         (beginning-of-line 1)
+         (looking-at python-nav-beginning-of-defun-regexp))))
+
+(defun python-info-current-line-comment-p ()
+  "Check if current line is a comment line."
+  (char-equal (or (char-after (+ (point) (current-indentation))) ?_) ?#))
+
+(defun python-info-current-line-empty-p ()
+  "Check if current line is empty, ignoring whitespace."
+  (save-excursion
+    (beginning-of-line 1)
+    (looking-at
+     (python-rx line-start (* whitespace)
+                (group (* not-newline))
+                (* whitespace) line-end))
+    (string-equal "" (match-string-no-properties 1))))
+
 \f
 ;;; Utility functions
 
@@ -2753,7 +2964,8 @@ to \"^python-\"."
    (buffer-local-variables from-buffer)))
 
 (defun python-util-forward-comment (&optional direction)
-  "Python mode specific version of `forward-comment'."
+  "Python mode specific version of `forward-comment'.
+Optional argument DIRECTION defines the direction to move to."
   (let ((comment-start (python-info-ppss-context 'comment))
         (factor (if (< (or direction 0) 0)
                     -99999
@@ -2764,7 +2976,7 @@ to \"^python-\"."
 
 \f
 ;;;###autoload
-(define-derived-mode python-mode fundamental-mode "Python"
+(define-derived-mode python-mode prog-mode "Python"
   "Major mode for editing Python files.
 
 \\{python-mode-map}
@@ -2779,10 +2991,14 @@ if that value is non-nil."
   (set (make-local-variable 'parse-sexp-lookup-properties) t)
   (set (make-local-variable 'parse-sexp-ignore-comments) t)
 
+  (set (make-local-variable 'forward-sexp-function)
+       'python-nav-forward-sexp-function)
+
   (set (make-local-variable 'font-lock-defaults)
-       '(python-font-lock-keywords
-         nil nil nil nil
-         (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords)))
+       '(python-font-lock-keywords nil nil nil nil))
+
+  (set (make-local-variable 'syntax-propertize-function)
+       python-syntax-propertize-function)
 
   (set (make-local-variable 'indent-line-function)
        #'python-indent-line-function)
@@ -2808,6 +3024,8 @@ if that value is non-nil."
   (set (make-local-variable 'add-log-current-defun-function)
        #'python-info-current-defun)
 
+  (add-hook 'which-func-functions #'python-info-current-defun nil t)
+
   (set (make-local-variable 'skeleton-further-elements)
        '((abbrev-mode nil)
          (< '(backward-delete-char-untabify (min python-indent-offset