diff options
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r-- | lisp/progmodes/python.el | 173 |
1 files changed, 108 insertions, 65 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index dba8fe5572b..d2bb82e0580 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -166,20 +166,20 @@ 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. - `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + (syntax-propertize-rules + (;; ĦBackrefs don't work in syntax-propertize-rules! + (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") - (1 (python-quote-syntax 1) nil lax) - (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) + (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 @@ -197,28 +197,25 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)." ;; 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) - (not (match-end 1))) ; prefix is null - (and (= n 1) ; prefix - (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. @@ -570,6 +567,33 @@ having to restart the program." "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 @@ -1302,13 +1326,9 @@ See `python-check-command' for the default." ;;;; 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) @@ -1386,6 +1406,23 @@ local value.") ;; 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? @@ -1408,14 +1445,12 @@ For running multiple processes in multiple buffers, see `run-python' and \\{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)) @@ -1512,12 +1547,12 @@ Don't save anything for STR matching `inferior-python-filter-regexp'." 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 @@ -1540,6 +1575,7 @@ buffer for a list of commands.)" (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) @@ -1557,8 +1593,10 @@ buffer for a list of commands.)" (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))) @@ -1614,7 +1652,12 @@ buffer for a list of commands.)" ;; 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) @@ -1814,7 +1857,9 @@ If there isn't, it's probably not appropriate to send input to return Eldoc 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.) @@ -2228,6 +2273,7 @@ the if condition." (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 @@ -2240,7 +2286,6 @@ the if condition." (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; @@ -2438,12 +2483,12 @@ with skeleton expansions for compound statement templates. :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) "# ") @@ -2530,9 +2575,7 @@ Runs `jython-mode-hook' after `python-mode-hook'." "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)) @@ -2550,7 +2593,7 @@ This function is appropriate for `comint-output-filter-functions'." 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))) @@ -2744,6 +2787,7 @@ comint believe the user typed this string so that (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. @@ -2783,6 +2827,7 @@ interaction between undo and process filters; the same problem exists in 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)) @@ -2799,10 +2844,9 @@ filter." )))) (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 @@ -2835,5 +2879,4 @@ filter." (provide 'python) (provide 'python-21) -;; arch-tag: 6fce1d99-a704-4de9-ba19-c6e4912b0554 ;;; python.el ends here |