;;; python.el --- silly walks for Python
-;; Copyright (C) 2003, 04 Free Software Foundation, Inc.
+;; Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
;; Author: Dave Love <fx@gnu.org>
;; Maintainer: FSF
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
(eval-when-compile
(require 'compile)
(autoload 'info-lookup-maybe-add-help "info-look"))
-(autoload 'compilation-start "compile")
(defgroup python nil
- "Silly walks in the Python language"
+ "Silly walks in the Python language."
:group 'languages
- :version "21.4"
+ :version "22.1"
:link '(emacs-commentary-link "python"))
\f
;;;###autoload
(defconst python-font-lock-syntactic-keywords
;; Make outer chars of matching triple-quote sequences into generic
;; string delimiters. Fixme: Is there a better way?
- `((,(rx (and (group (optional (any "uUrR"))) ; prefix gets syntax property
+ `((,(rx (and (or line-start buffer-start (not (syntax escape))) ; avoid escaped
+ ; leading quote
+ (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
;; ur"""ar""" x='"' # """
;; x = ''' """ ' a
;; '''
- ;; x '"""' x
+ ;; x '"""' x """ \"""" x
(save-excursion
(goto-char (match-beginning 0))
- (unless (eq ?\\ (char-before))
- (cond
- ;; Consider property for the last char if in a fenced string.
- ((= n 3)
- (let ((syntax (syntax-ppss)))
- (when (eq t (nth 3 syntax)) ; after unclosed fence
- (goto-char (nth 8 syntax)) ; fence position
- ;; Skip any prefix.
- (if (memq (char-after) '(?u ?U ?R ?r))
- (skip-chars-forward "uUrR"))
- ;; 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) ; not prefix
- (= (match-beginning 1) (match-end 1))) ; prefix is null
- (and (= n 1) ; prefix
- (/= (match-beginning 1) (match-end 1)))) ; non-empty
- (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.
- )))
+ (cond
+ ;; Consider property for the last char if in a fenced string.
+ ((= n 3)
+ (let ((syntax (syntax-ppss)))
+ (when (eq t (nth 3 syntax)) ; after unclosed fence
+ (goto-char (nth 8 syntax)) ; fence position
+ ;; Skip any prefix.
+ (if (memq (char-after) '(?u ?U ?R ?r))
+ (skip-chars-forward "uUrR"))
+ ;; 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) ; not prefix
+ (= (match-beginning 1) (match-end 1))) ; prefix is null
+ (and (= n 1) ; prefix
+ (/= (match-beginning 1) (match-end 1)))) ; non-empty
+ (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.
+ )))
;; This isn't currently in `font-lock-defaults' as probably not worth
;; it -- we basically only mess with a few normally-symbol characters.
(syntax-ppss (line-beginning-position)))))))
(defun python-comment-line-p ()
- "Return non-nil if current line has only a comment or is blank."
+ "Return non-nil iff current line has only a comment."
(save-excursion
(end-of-line)
- ;; FIXME: This looks wrong because it returns nil for empty lines. --Stef
(when (eq 'comment (syntax-ppss-context (syntax-ppss)))
(back-to-indentation)
(looking-at (rx (or (syntax comment-start) line-end))))))
line-end))
(save-excursion (python-end-of-statement))
t)
- (not (python-in-string/comment)))))
+ (not (progn (goto-char (match-beginning 0))
+ (python-in-string/comment))))))
(defun python-close-block-statement-p (&optional bos)
"Return non-nil if current line is a statement closing a block.
(unless bos (python-beginning-of-statement))
(back-to-indentation)
(looking-at (rx (and (or "return" "raise" "break" "continue" "pass")
- word-end)))))
+ symbol-end)))))
(defun python-outdent-p ()
"Return non-nil if current line should outdent a level."
(save-excursion
(back-to-indentation)
- (and (looking-at (rx (and (or (and (or "else" "finally") word-end)
- (and (or "except" "elif") word-end
+ (and (looking-at (rx (and (or (and (or "else" "finally") symbol-end)
+ (and (or "except" "elif") symbol-end
(1+ (not (any ?:)))))
(optional space) ":" (optional space)
(or (syntax comment-start) line-end))))
;; Fixme: check this
(not (looking-at (rx (and (or (and (or "if" "elif" "except"
"for" "while")
- word-end (1+ (not (any ?:))))
- (and "try" word-end))
+ symbol-end (1+ (not (any ?:))))
+ (and "try" symbol-end))
(optional space) ":" (optional space)
(or (syntax comment-start) line-end)))))
(progn (end-of-line)
expressions."
(beginning-of-line)
(python-beginning-of-string)
- (while (python-continuation-line-p)
- (beginning-of-line)
- (if (python-backslash-continuation-line-p)
- (while (python-backslash-continuation-line-p)
- (forward-line -1))
- (python-beginning-of-string)
- ;; Skip forward out of nested brackets.
- (condition-case () ; beware invalid syntax
- (progn (backward-up-list (syntax-ppss-depth (syntax-ppss))) t)
- (error (end-of-line)))))
+ (catch 'foo
+ (while (python-continuation-line-p)
+ (beginning-of-line)
+ (if (python-backslash-continuation-line-p)
+ (while (python-backslash-continuation-line-p)
+ (forward-line -1))
+ (python-beginning-of-string)
+ ;; Skip forward out of nested brackets.
+ (condition-case () ; beware invalid syntax
+ (progn (backward-up-list (syntax-ppss-depth (syntax-ppss))) t)
+ (error (throw 'foo nil))))))
(back-to-indentation))
(defun python-end-of-statement ()
(if name
(file-name-nondirectory name))))))))
(setq python-saved-check-command command)
+ (require 'compile) ;To define compilation-* variables.
(save-some-buffers (not compilation-ask-about-save) nil)
(let ((compilation-error-regexp-alist
(cons '("(\\([^,]+\\), line \\([0-9]+\\))" 1 2)
(let ((map (make-sparse-keymap)))
;; This will inherit from comint-mode-map.
(define-key map "\C-c\C-l" 'python-load-file)
- (define-key map "\C-c\C-z" 'python-switch-to-python) ;What for? --Stef
(define-key map "\C-c\C-v" 'python-check)
;; Note that we _can_ still use these commands which send to the
;; Python process even at the prompt iff we have a normal prompt,
;; Still required by `comint-redirect-send-command', for instance
;; (and we need to match things like `>>> ... >>> '):
(set (make-local-variable 'comint-prompt-regexp)
- (rx (and line-start (1+ (and (repeat 3 (any ">.")) ?\ )))))
+ (rx (and line-start (1+ (and (repeat 3 (any ">.")) ?\s)))))
(set (make-local-variable 'compilation-error-regexp-alist)
python-compilation-regexp-alist)
(compilation-shell-minor-mode 1))
(defvar python-preoutput-continuation nil
"If non-nil, funcall this when `python-preoutput-filter' sees `_emacs_ok'.")
+(defvar python-preoutput-leftover nil)
+
;; Using this stops us getting lines in the buffer like
;; >>> ... ... >>>
;; Also look for (and delete) an `_emacs_ok' string and call
;; `python-preoutput-continuation' if we get it.
(defun python-preoutput-filter (s)
"`comint-preoutput-filter-functions' function: ignore prompts not at bol."
+ (when python-preoutput-leftover
+ (setq s (concat python-preoutput-leftover s))
+ (setq python-preoutput-leftover nil))
(cond ((and (string-match (rx (and string-start (repeat 3 (any ".>"))
- " " string-end))
- s)
- (/= (let ((inhibit-field-text-motion t))
- (line-beginning-position))
- (point)))
+ " " string-end))
+ s)
+ (/= (let ((inhibit-field-text-motion t))
+ (line-beginning-position))
+ (point)))
+ "")
+ ((string= s "_emacs_ok\n")
+ (when python-preoutput-continuation
+ (funcall python-preoutput-continuation)
+ (setq python-preoutput-continuation nil))
+ "")
+ ((string-match "_emacs_out \\(.*\\)\n" s)
+ (setq python-preoutput-result (match-string 1 s))
+ "")
+ ((string-match ".*\n" s)
+ s)
+ ((or (eq t (compare-strings s nil nil "_emacs_ok\n" nil (length s)))
+ (let ((end (min (length "_emacs_out ") (length s))))
+ (eq t (compare-strings s nil end "_emacs_out " nil end))))
+ (setq python-preoutput-leftover s)
"")
- ((string= s "_emacs_ok\n")
- (when python-preoutput-continuation
- (funcall python-preoutput-continuation)
- (setq python-preoutput-continuation nil))
- "")
- ((string-match "_emacs_out \\(.*\\)\n" s)
- (setq python-preoutput-result (match-string 1 s))
- "")
- (t s)))
+ (t s)))
;;;###autoload
(defun run-python (&optional cmd noshow)
buffer automatically.
If there is a process already running in `*Python*', switch to
that buffer. Interactively, a prefix arg allows you to edit the initial
-command line (default is `python-command'); `-i' etc. args will be added
+command line (default is `python-command'); `-i' etc. args will be added
to this as appropriate. Runs the hook `inferior-python-mode-hook'
\(after the `comint-mode-hook' is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
;; (not a name) in Python buffers from which `run-python' &c is
;; invoked. Would support multiple processes better.
(unless (comint-check-proc python-buffer)
- (let ((cmdlist (append (python-args-to-list cmd) '("-i")))
- (process-environment ; to import emacs.py
- (push (concat "PYTHONPATH=" data-directory)
- process-environment)))
+ (let* ((cmdlist (append (python-args-to-list cmd) '("-i")))
+ (path (getenv "PYTHONPATH"))
+ (process-environment ; to import emacs.py
+ (cons (concat "PYTHONPATH=" data-directory
+ (if path (concat ":" path)))
+ process-environment)))
(set-buffer (apply 'make-comint "Python" (car cmdlist) nil
(cdr cmdlist)))
- (setq python-buffer "*Python*"))
+ (setq python-buffer (buffer-name)))
(inferior-python-mode)
;; Load function defintions we need.
;; Before the preoutput function was used, this was done via -c in
(set-marker orig-start (line-beginning-position 0)))
(write-region "if True:\n" nil f nil 'nomsg))
(write-region start end f t 'nomsg)
- (let ((proc (python-proc))) ;Make sure we're running a process.
- (with-current-buffer python-buffer
- (python-send-command command)
- ;; Tell compile.el to redirect error locations in file `f' to
- ;; positions past marker `orig-start'. It has to be done *after*
- ;; python-send-command's call to compilation-forget-errors.
- (compilation-fake-loc orig-start f)))))
+ (with-current-buffer (process-buffer (python-proc)) ;Runs python if needed.
+ (python-send-command command)
+ ;; Tell compile.el to redirect error locations in file `f' to
+ ;; positions past marker `orig-start'. It has to be done *after*
+ ;; python-send-command's call to compilation-forget-errors.
+ (compilation-fake-loc orig-start f))))
(defun python-send-string (string)
"Evaluate STRING in inferior Python process."
"Switch to the Python process buffer.
With prefix arg, position cursor at end of buffer."
(interactive "P")
- (if (get-buffer python-buffer)
- (pop-to-buffer python-buffer)
- (error "No current process buffer. See variable `python-buffer'"))
+ (pop-to-buffer (process-buffer (python-proc))) ;Runs python if needed.
(when eob-p
(push-mark)
(goto-char (point-max))))
-(add-to-list 'debug-ignored-errors "^No current process buffer.")
-
(defun python-send-region-and-go (start end)
"Send the region to the inferior Python process.
Then switch to the process buffer."
(comint-check-source file-name) ; Check to see if buffer needs saving.
(setq python-prev-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
- (let ((proc (python-proc))) ;Make sure we have a process.
- (with-current-buffer python-buffer
- ;; Fixme: I'm not convinced by this logic from python-mode.el.
- (python-send-command
- (if (string-match "\\.py\\'" file-name)
- ;; Fixme: make sure the directory is in the path list
- (let ((module (file-name-sans-extension
- (file-name-nondirectory file-name))))
- (format "emacs.eimport(%S,%S)"
- module (file-name-directory file-name)))
- (format "execfile(%S)" file-name)))
- (message "%s loaded" file-name))))
+ (with-current-buffer (process-buffer (python-proc)) ;Runs python if needed.
+ ;; Fixme: I'm not convinced by this logic from python-mode.el.
+ (python-send-command
+ (if (string-match "\\.py\\'" file-name)
+ (let ((module (file-name-sans-extension
+ (file-name-nondirectory file-name))))
+ (format "emacs.eimport(%S,%S)"
+ module (file-name-directory file-name)))
+ (format "execfile(%S)" file-name)))
+ (message "%s loaded" file-name)))
;; Fixme: If we need to start the process, wait until we've got the OK
;; from the startup.
Otherwise inherits from `python-mode-syntax-table'.")
(defvar view-return-to-alist)
+(eval-when-compile (autoload 'help-buffer "help-fns"))
;; Fixme: Should this actually be used instead of info-look, i.e. be
;; bound to C-h S? Can we use other pydoc stuff before python 2.2?
(toggle-read-only 1)
(setq view-return-to-alist
(list (cons (selected-window) help-return-method))))))
- (help-setup-xref (list 'python-describe-symbol symbol))
+ (help-setup-xref (list 'python-describe-symbol symbol) (interactive-p))
(with-output-to-temp-buffer (help-buffer)
(with-current-buffer standard-output
(set (make-local-variable 'comint-redirect-subvert-readonly) t)
(let ((proc (python-proc)))
(python-send-string string)
(setq python-preoutput-result nil)
- (accept-process-output proc 5)
+ (while (progn
+ (accept-process-output proc 5)
+ python-preoutput-leftover))
python-preoutput-result))
;; Fixme: try to make it work with point in the arglist. Also, is
(string-match "^Python \\([0-9]+\\.[0-9]+\\>\\)" s)
(match-string 1 s)))
;; Whether info files have a Python version suffix, e.g. in Debian.
- (versioned
+ (versioned
(with-temp-buffer
(with-no-warnings (Info-mode))
(condition-case ()
;; Don't use `info' because it would pop-up a *info* buffer.
(with-no-warnings
(Info-goto-node (format "(python%s-lib)Miscellaneous Index"
- version)))
+ version))
+ t)
(error nil)))))
(info-lookup-maybe-add-help
:mode 'python-mode
(beginning-of-defun)
(if (looking-at (rx (and (0+ space) (or "def" "class") (1+ space)
(group (1+ (or word (syntax symbol))))
- word-end)))
+ ;; Greediness makes this unnecessary? --Stef
+ symbol-end)))
(push (match-string 1) accum)))
(if accum (mapconcat 'identity accum ".")))))
;;;; Modes.
(defvar outline-heading-end-regexp)
-(defvar eldoc-print-current-symbol-info-function)
+(defvar eldoc-documentation-function)
;;;###autoload
(define-derived-mode python-mode fundamental-mode "Python"
'(python-font-lock-keywords nil nil ((?_ . "w")) 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 'parse-sexp-lookup-properties) t)
(set (make-local-variable 'comment-start) "# ")
(set (make-local-variable 'indent-line-function) #'python-indent-line)
(set (make-local-variable 'paragraph-start) "\\s-*$")
(set (make-local-variable 'fill-paragraph-function) 'python-fill-paragraph)
- (set (make-local-variable 'require-final-newline) t)
+ (set (make-local-variable 'require-final-newline) mode-require-final-newline)
(set (make-local-variable 'add-log-current-defun-function)
#'python-current-defun)
;; Fixme: Generalize to do all blocks?
'python-beginning-of-defun)
(set (make-local-variable 'end-of-defun-function) 'python-end-of-defun)
(setq imenu-create-index-function #'python-imenu-create-index)
- (set (make-local-variable 'eldoc-print-current-symbol-info-function)
+ (set (make-local-variable 'eldoc-documentation-function)
#'python-eldoc-function)
(add-hook 'eldoc-mode-hook
'(lambda () (run-python 0 t)) nil t) ; need it running