;;; python.el --- silly walks for Python -*- coding: iso-8859-1 -*-
-;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Dave Love <fx@gnu.org>
;; `forward-into-nomenclature' should be done separately, since it's
;; not specific to Python, and I've installed a minor mode to do the
;; job properly in Emacs 23. [CC mode 5.31 contains an incompatible
-;; feature, `c-subword-mode' which is intended to have a similar
+;; feature, `subword-mode' which is intended to have a similar
;; effect, but actually only affects word-oriented keybindings.]
;; Other things seem more natural or canonical here, e.g. the
(require 'compile)
(require 'hippie-exp))
-(require 'sym-comp)
(autoload 'comint-mode "comint")
(defgroup python nil
:link '(emacs-commentary-link "python"))
\f
;;;###autoload
-(add-to-list 'interpreter-mode-alist '("jython" . jython-mode))
+(add-to-list 'interpreter-mode-alist (cons (purecopy "jython") 'jython-mode))
;;;###autoload
-(add-to-list 'interpreter-mode-alist '("python" . python-mode))
+(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode))
;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
-(add-to-list 'same-window-buffer-names "*Python*")
+(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'") 'python-mode))
+(add-to-list 'same-window-buffer-names (purecopy "*Python*"))
\f
;;;; Font lock
(defvar python-font-lock-keywords
`(,(rx symbol-start
- ;; From v 2.5 reference, § keywords.
+ ;; From v 2.7 reference, § keywords.
;; def and class dealt with separately below
(or "and" "as" "assert" "break" "continue" "del" "elif" "else"
"except" "exec" "finally" "for" "from" "global" "if"
;; Not real keywords, but close enough to be fontified as such
"self" "True" "False")
symbol-end)
- (,(rx symbol-start "None" symbol-end) ; see § Keywords in 2.5 manual
+ (,(rx symbol-start "None" symbol-end) ; see § Keywords in 2.7 manual
. font-lock-constant-face)
;; Definitions
(,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_))))
;; Top-level assignments are worth highlighting.
(,(rx line-start (group (1+ (or word ?_))) (0+ space) "=")
(1 font-lock-variable-name-face))
- (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)))) ; decorators
+ ;; Decorators.
+ (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_))
+ (0+ "." (1+ (or word ?_)))))
(1 font-lock-type-face))
;; Built-ins. (The next three blocks are from
- ;; `__builtin__.__dict__.keys()' in Python 2.5.1.) These patterns
+ ;; `__builtin__.__dict__.keys()' in Python 2.7) These patterns
;; are debateable, but they at least help to spot possible
;; shadowing of builtins.
(,(rx symbol-start (or
"SystemExit" "TabError" "TypeError" "UnboundLocalError"
"UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError"
"UnicodeTranslateError" "UnicodeWarning" "UserWarning"
- "ValueError" "Warning" "ZeroDivisionError") symbol-end)
+ "ValueError" "Warning" "ZeroDivisionError"
+ ;; Python 2.7
+ "BufferError" "BytesWarning" "WindowsError") symbol-end)
. font-lock-type-face)
(,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start
(group (or
"range" "raw_input" "reduce" "reload" "repr" "reversed"
"round" "set" "setattr" "slice" "sorted" "staticmethod"
"str" "sum" "super" "tuple" "type" "unichr" "unicode" "vars"
- "xrange" "zip")) symbol-end)
+ "xrange" "zip"
+ ;; Python 2.7.
+ "bin" "bytearray" "bytes" "format" "memoryview" "next" "print"
+ )) symbol-end)
(1 font-lock-builtin-face))
(,(rx symbol-start (or
;; other built-ins
"True" "False" "None" "Ellipsis"
- "_" "__debug__" "__doc__" "__import__" "__name__") symbol-end)
+ "_" "__debug__" "__doc__" "__import__" "__name__" "__package__")
+ symbol-end)
. font-lock-builtin-face)))
-(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.
- `((,(rx (not (any ?\\))
- ?\\ (* (and ?\\ ?\\))
- (group (syntax string-quote))
- (backref 1)
- (group (backref 1)))
- (2 ,(string-to-syntax "\""))) ; dummy
- (,(rx (group (optional (any "uUrR"))) ; prefix gets syntax property
- (optional (any "rR")) ; possible second prefix
- (group (syntax string-quote)) ; maybe gets property
- (backref 2) ; per first quote
- (group (backref 2))) ; maybe gets property
- (1 (python-quote-syntax 1))
- (2 (python-quote-syntax 2))
- (3 (python-quote-syntax 3)))
- ;; This doesn't really help.
-;;; (,(rx (and ?\\ (group ?\n))) (1 " "))
- ))
-
-(defun python-quote-syntax (n)
+ (syntax-propertize-rules
+ (;; (rx (not (any ?\\))
+ ;; ?\\ (* (and ?\\ ?\\))
+ ;; (group (syntax string-quote))
+ ;; (backref 1)
+ ;; (group (backref 1)))
+ ;; ¡Backrefs don't work in syntax-propertize-rules!
+ "[^\\]\\\\\\(\\\\\\\\\\)*\\(?:''\\('\\)\\|\"\"\\(?2:\"\\)\\)"
+ (2 "\"")) ; dummy
+ (;; (rx (optional (group (any "uUrR"))) ; prefix gets syntax property
+ ;; (optional (any "rR")) ; possible second prefix
+ ;; (group (syntax string-quote)) ; maybe gets property
+ ;; (backref 2) ; per first quote
+ ;; (group (backref 2))) ; maybe gets property
+ ;; ¡Backrefs don't work in syntax-propertize-rules!
+ "\\([RUru]\\)?[Rr]?\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)"
+ (3 (ignore (python-quote-syntax))))
+ ;; This doesn't really help.
+ ;;((rx (and ?\\ (group ?\n))) (1 " "))
+ ))
+
+(defun python-quote-syntax ()
"Put `syntax-table' property correctly on triple quote.
Used for syntactic keywords. N is the match number (1, 2 or 3)."
;; Given a triple quote, we have to check the context to know
;; x '"""' x """ \"""" x
(save-excursion
(goto-char (match-beginning 0))
- (cond
- ;; Consider property for the last char if in a fenced string.
- ((= n 3)
- (let* ((font-lock-syntactic-keywords nil)
- (syntax (syntax-ppss)))
- (when (eq t (nth 3 syntax)) ; after unclosed fence
- (goto-char (nth 8 syntax)) ; fence position
- (skip-chars-forward "uUrR") ; skip any prefix
- ;; Is it a matching sequence?
- (if (eq (char-after) (char-after (match-beginning 2)))
- (eval-when-compile (string-to-syntax "|"))))))
- ;; Consider property for initial char, accounting for prefixes.
- ((or (and (= n 2) ; leading quote (not prefix)
- (= (match-beginning 1) (match-end 1))) ; prefix is null
- (and (= n 1) ; prefix
- (/= (match-beginning 1) (match-end 1)))) ; non-empty
- (let ((font-lock-syntactic-keywords nil))
- (unless (eq 'string (syntax-ppss-context (syntax-ppss)))
- (eval-when-compile (string-to-syntax "|")))))
- ;; Otherwise (we're in a non-matching string) the property is
- ;; nil, which is OK.
- )))
+ (let ((syntax (save-match-data (syntax-ppss))))
+ (cond
+ ((eq t (nth 3 syntax)) ; after unclosed fence
+ ;; Consider property for the last char if in a fenced string.
+ (goto-char (nth 8 syntax)) ; fence position
+ (skip-chars-forward "uUrR") ; skip any prefix
+ ;; Is it a matching sequence?
+ (if (eq (char-after) (char-after (match-beginning 2)))
+ (put-text-property (match-beginning 3) (match-end 3)
+ 'syntax-table (string-to-syntax "|"))))
+ ((match-end 1)
+ ;; Consider property for initial char, accounting for prefixes.
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'syntax-table (string-to-syntax "|")))
+ (t
+ ;; Consider property for initial char, accounting for prefixes.
+ (put-text-property (match-beginning 2) (match-end 2)
+ 'syntax-table (string-to-syntax "|"))))
+ )))
;; This isn't currently in `font-lock-defaults' as probably not worth
;; it -- we basically only mess with a few normally-symbol characters.
(define-key map "\C-c\C-z" 'python-switch-to-python)
(define-key map "\C-c\C-m" 'python-load-file)
(define-key map "\C-c\C-l" 'python-load-file) ; a la cmuscheme
- (substitute-key-definition 'complete-symbol 'symbol-complete
+ (substitute-key-definition 'complete-symbol 'completion-at-point
map global-map)
(define-key map "\C-c\C-i" 'python-find-imports)
(define-key map "\C-c\C-t" 'python-expand-template)
"-"
["Help on symbol" python-describe-symbol
:help "Use pydoc on symbol at point"]
- ["Complete symbol" symbol-complete
+ ["Complete symbol" completion-at-point
:help "Complete (qualified) symbol before point"]
["Find function" python-find-function
:help "Try to find source definition of function at point"]
"Queue of Python temp files awaiting execution.
Currently-active file is at the head of the list.")
+(defcustom python-shell-prompt-alist
+ '(("ipython" . "^In \\[[0-9]+\\]: *")
+ (t . "^>>> "))
+ "Alist of Python input prompts.
+Each element has the form (PROGRAM . REGEXP), where PROGRAM is
+the value of `python-python-command' for the python process and
+REGEXP is a regular expression matching the Python prompt.
+PROGRAM can also be t, which specifies the default when no other
+element matches `python-python-command'."
+ :type 'string
+ :group 'python
+ :version "24.1")
+
+(defcustom python-shell-continuation-prompt-alist
+ '(("ipython" . "^ [.][.][.]+: *")
+ (t . "^[.][.][.] "))
+ "Alist of Python continued-line prompts.
+Each element has the form (PROGRAM . REGEXP), where PROGRAM is
+the value of `python-python-command' for the python process and
+REGEXP is a regular expression matching the Python prompt for
+continued lines.
+PROGRAM can also be t, which specifies the default when no other
+element matches `python-python-command'."
+ :type 'string
+ :group 'python
+ :version "24.1")
+
(defvar python-pdbtrack-is-tracking-p nil)
(defconst python-pdbtrack-stack-entry-regexp
- "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()"
+ "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()"
"Regular expression pdbtrack uses to find a stack trace entry.")
(defconst python-pdbtrack-input-prompt "\n[(<]*[Pp]db[>)]+ "
'(("else" "if" "elif" "while" "for" "try" "except")
("elif" "if" "elif")
("except" "try" "except")
- ("finally" "try" "except"))
+ ("finally" "else" "try" "except"))
"Alist of keyword matches.
The car of an element is a keyword introducing a statement which
can close a block opened by a keyword in the cdr.")
\f
;;;; Inferior mode stuff (following cmuscheme).
-;; Fixme: Make sure we can work with IPython.
-
(defcustom python-python-command "python"
"Shell command to run Python interpreter.
-Any arguments can't contain whitespace.
-Note that IPython may not work properly; it must at least be used
-with the `-cl' flag, i.e. use `ipython -cl'."
+Any arguments can't contain whitespace."
:group 'python
:type 'string)
;; Autoloaded.
(declare-function compilation-shell-minor-mode "compile" (&optional arg))
+(defvar python--prompt-regexp nil)
+
+(defun python--set-prompt-regexp ()
+ (let ((prompt (cdr-safe (or (assoc python-python-command
+ python-shell-prompt-alist)
+ (assq t python-shell-prompt-alist))))
+ (cprompt (cdr-safe (or (assoc python-python-command
+ python-shell-continuation-prompt-alist)
+ (assq t python-shell-continuation-prompt-alist)))))
+ (set (make-local-variable 'comint-prompt-regexp)
+ (concat "\\("
+ (mapconcat 'identity
+ (delq nil (list prompt cprompt "^([Pp]db) "))
+ "\\|")
+ "\\)"))
+ (set (make-local-variable 'python--prompt-regexp) prompt)))
+
;; Fixme: This should inherit some stuff from `python-mode', but I'm
;; not sure how much: at least some keybindings, like C-c C-f;
;; syntax?; font-locking, e.g. for triple-quoted strings?
\\{inferior-python-mode-map}"
:group 'python
+ (require 'ansi-color) ; for ipython
(setq mode-line-process '(":%s"))
(set (make-local-variable 'comint-input-filter) 'python-input-filter)
(add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter
nil t)
- ;; Still required by `comint-redirect-send-command', for instance
- ;; (and we need to match things like `>>> ... >>> '):
- (set (make-local-variable 'comint-prompt-regexp)
- (rx line-start (1+ (and (or (repeat 3 (any ">.")) "(Pdb)") " "))))
+ (python--set-prompt-regexp)
(set (make-local-variable 'compilation-error-regexp-alist)
python-compilation-regexp-alist)
(compilation-shell-minor-mode 1))
cmd)))
(unless (shell-command-to-string cmd)
(error "Can't run Python command `%s'" cmd))
- (let* ((res (shell-command-to-string (concat cmd " --version"))))
- (string-match "Python \\([0-9]\\)\\.\\([0-9]\\)" res)
- (unless (and (equal "2" (match-string 1 res))
- (match-beginning 2)
- (>= (string-to-number (match-string 2 res)) 2))
- (error "Only Python versions >= 2.2 and < 3.0 supported")))
+ (let* ((res (shell-command-to-string
+ (concat cmd
+ " -c \"from sys import version_info;\
+print version_info >= (2, 2) and version_info < (3, 0)\""))))
+ (unless (string-match "True" res)
+ (error "Only Python versions >= 2.2 and < 3.0 are supported")))
(setq python-version-checked t)))
;;;###autoload
(interactive (if current-prefix-arg
(list (read-string "Run Python: " python-command) nil t)
(list python-command)))
+ (require 'ansi-color) ; for ipython
(unless cmd (setq cmd python-command))
(python-check-version cmd)
(setq python-command cmd)
(if path (concat path path-separator))
data-directory)
process-environment))
- ;; Suppress use of pager for help output:
- (process-connection-type nil))
+ ;; If we use a pipe, unicode characters are not printed
+ ;; correctly (Bug#5794) and IPython does not work at
+ ;; all (Bug#5390).
+ (process-connection-type t))
(apply 'make-comint-in-buffer "Python"
(generate-new-buffer "*Python*")
(car cmdlist) nil (cdr cmdlist)))
;; non-ASCII.
(interactive "r")
(let* ((f (make-temp-file "py"))
- (command (format "emacs.eexecfile(%S)" f))
+ (command
+ ;; IPython puts the FakeModule module into __main__ so
+ ;; emacs.eexecfile becomes useless.
+ (if (string-match "^ipython" python-command)
+ (format "execfile %S" f)
+ (format "emacs.eexecfile(%S)" f)))
(orig-start (copy-marker start)))
(when (save-excursion
(goto-char start)
information etc. If PROC is non-nil, check the buffer for that process."
(with-current-buffer (process-buffer (or proc (python-proc)))
(save-excursion
- (save-match-data (re-search-backward ">>> \\=" nil t)))))
+ (save-match-data
+ (re-search-backward (concat python--prompt-regexp " *\\=")
+ nil t)))))
;; Fixme: Is there anything reasonable we can do with random methods?
;; (Currently only works with functions.)
"Return a list of completions of the string SYMBOL from Python process.
The list is sorted.
Uses `python-imports' to load modules against which to complete."
- (when symbol
+ (when (stringp symbol)
(let ((completions
(condition-case ()
(car (read-from-string
(python-send-receive
- (format "emacs.complete(%S,%s)" symbol python-imports))))
+ (format "emacs.complete(%S,%s)"
+ (substring-no-properties symbol)
+ python-imports))))
(error nil))))
(sort
;; We can get duplicates from the above -- don't know why.
(delete-dups completions)
#'string<))))
-(defun python-partial-symbol ()
- "Return the partial symbol before point (for completion)."
+(defun python-completion-at-point ()
(let ((end (point))
(start (save-excursion
(and (re-search-backward
(group (1+ (regexp "[[:alnum:]._]"))) point)
nil t)
(match-beginning 1)))))
- (if start (buffer-substring-no-properties start end))))
+ (when start
+ (list start end
+ (completion-table-dynamic 'python-symbol-completions)))))
\f
;;;; FFAP support
(eval-when-compile
;; Define a user-level skeleton and add it to the abbrev table.
(defmacro def-python-skeleton (name &rest elements)
+ (declare (indent 2))
(let* ((name (symbol-name name))
(function (intern (concat "python-insert-" name))))
`(progn
(define-skeleton ,function
,(format "Insert Python \"%s\" template." name)
,@elements)))))
-(put 'def-python-skeleton 'lisp-indent-function 2)
;; From `skeleton-further-elements' set below:
;; `<': outdent a level;
:group 'python
(set (make-local-variable 'font-lock-defaults)
'(python-font-lock-keywords nil nil nil nil
- (font-lock-syntactic-keywords
- . python-font-lock-syntactic-keywords)
- ;; This probably isn't worth it.
- ;; (font-lock-syntactic-face-function
- ;; . python-font-lock-syntactic-face-function)
- ))
+ ;; This probably isn't worth it.
+ ;; (font-lock-syntactic-face-function
+ ;; . python-font-lock-syntactic-face-function)
+ ))
+ (set (make-local-variable 'syntax-propertize-function)
+ python-syntax-propertize-function)
(set (make-local-variable 'parse-sexp-lookup-properties) t)
(set (make-local-variable 'parse-sexp-ignore-comments) t)
(set (make-local-variable 'comment-start) "# ")
(add-hook 'eldoc-mode-hook
(lambda () (run-python nil t)) ; need it running
nil t)
- (set (make-local-variable 'symbol-completion-symbol-function)
- 'python-partial-symbol)
- (set (make-local-variable 'symbol-completion-completions-function)
- 'python-symbol-completions)
+ (add-hook 'completion-at-point-functions
+ 'python-completion-at-point nil 'local)
;; Fixme: should be in hideshow. This seems to be of limited use
;; since it isn't (can't be) indentation-based. Also hide-level
;; doesn't seem to work properly.
'((< '(backward-delete-char-untabify (min python-indent
(current-column))))
(^ '(- (1+ (current-indentation))))))
- ;; Let's not mess with hippie-expand. Symbol-completion should rather be
- ;; bound to another key, since it has different performance requirements.
- ;; (if (featurep 'hippie-exp)
- ;; (set (make-local-variable 'hippie-expand-try-functions-list)
- ;; (cons 'symbol-completion-try-complete
- ;; hippie-expand-try-functions-list)))
;; Python defines TABs as being 8-char wide.
(set (make-local-variable 'tab-width) 8)
(unless font-lock-mode (font-lock-mode 1))
"Watch output for Python prompt and exec next file waiting in queue.
This function is appropriate for `comint-output-filter-functions'."
;; TBD: this should probably use split-string
- (when (and (or (string-equal string ">>> ")
- (and (>= (length string) 5)
- (string-equal (substring string -5) "\n>>> ")))
+ (when (and (string-match python--prompt-regexp string)
python-file-queue)
(condition-case nil
(delete-file (car python-file-queue))
overlay-arrow-string "=>"
python-pdbtrack-is-tracking-p t)
(set-marker overlay-arrow-position
- (save-excursion (beginning-of-line) (point))
+ (line-beginning-position)
(current-buffer)))
(setq overlay-arrow-position nil
python-pdbtrack-is-tracking-p nil)))
;; Add in number of lines for leading '##' comments:
(setq lineno
(+ lineno
- (save-excursion
- (set-buffer funcbuffer)
+ (with-current-buffer funcbuffer
(if (equal (point-min)(point-max))
0
(count-lines
(while (and buffers (not got))
(setq buf (car buffers)
buffers (cdr buffers))
- (if (and (save-excursion (set-buffer buf)
- (string= major-mode "python-mode"))
+ (if (and (with-current-buffer buf
+ (string= major-mode "python-mode"))
(or (string-match funcname (buffer-name buf))
(string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
funcname "\\s-*(")
- (save-excursion
- (set-buffer buf)
+ (with-current-buffer buf
(buffer-substring (point-min)
(point-max))))))
(setq got buf)))
;; add some comment, so that we can filter it out of history
(cmd (format "execfile(r'%s') # PYTHON-MODE\n" filename)))
(unwind-protect
- (save-excursion
- (set-buffer procbuf)
+ (with-current-buffer procbuf
(goto-char (point-max))
(move-marker (process-mark proc) (point))
(funcall (process-filter proc) proc msg))
(set-buffer curbuf))
(process-send-string proc cmd)))
+
;;;###autoload
(defun python-shell (&optional argprompt)
"Start an interactive Python interpreter in another window.
non-Python process buffers using the default (Emacs-supplied) process
filter."
(interactive "P")
+ (require 'ansi-color) ; For ipython
;; Set the default shell if not already set
(when (null python-which-shell)
(python-toggle-shells python-default-interpreter))
))))
(switch-to-buffer-other-window
(apply 'make-comint python-which-bufname python-which-shell nil args))
- (make-local-variable 'comint-prompt-regexp)
(set-process-sentinel (get-buffer-process (current-buffer))
'python-sentinel)
- (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ")
+ (python--set-prompt-regexp)
(add-hook 'comint-output-filter-functions
'python-comint-output-filter-function nil t)
;; pdbtrack
(provide 'python)
(provide 'python-21)
-;; arch-tag: 6fce1d99-a704-4de9-ba19-c6e4912b0554
;;; python.el ends here