]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/python.el
Merge from emacs-23
[gnu-emacs] / lisp / progmodes / python.el
index dba8fe5572b56450240b2b6d96e96543642ae5fc..d2bb82e0580a6aa0333af332c35064852d3e5616 100644 (file)
           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."
 \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)
 
@@ -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