-;;; 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, 2012 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
;; 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'"))
;; 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 ?. ?_)))
(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.
- "\\(?:\\('\\)'\\('\\)\\|\\(?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.
: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.")
(defun python-indent-guess-indent-offset ()
"Guess and set `python-indent-offset' for the current buffer."
+ (interactive)
(save-excursion
(save-restriction
(widen)
((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
('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
(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.
;; 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))
(python-info-ppss-context-type))
(forward-line 1)))))))
-(defun python-nav-sentence-start ()
- "Move to start of current sentence."
+(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
(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))
(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-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)
(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
(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)
(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.
(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."
(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)
: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.")
"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
(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)
(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."
(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))
(when (equal (char-before) ?\\)
(point-marker)))))
-(defun python-info-beginning-of-backlash (&optional line-number)
- "Return the point where the backlashed line start.
+(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
(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
\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}
(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)