]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/python.el
Merge multi-tty branch
[gnu-emacs] / lisp / progmodes / python.el
index 66d8cd4714f9f9425df57944569761f37fb19a84..462445f3d71844629c9ca8970d9336993809ccfe 100644 (file)
@@ -1,6 +1,6 @@
 ;;; python.el --- silly walks for Python
 
-;; Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
 
 ;; Author: Dave Love <fx@gnu.org>
 ;; Maintainer: FSF
@@ -11,7 +11,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -67,7 +67,8 @@
 (eval-when-compile
   (require 'cl)
   (require 'compile)
-  (require 'comint))
+  (require 'comint)
+  (require 'hippie-exp))
 
 (autoload 'comint-mode "comint")
 
@@ -95,7 +96,9 @@
             "import" "in" "is" "lambda" "not" "or" "pass" "print"
             "raise" "return" "try" "while" "yield"
             ;; Future keywords
-            "as" "None")
+            "as" "None" "with"
+             ;; Not real keywords, but close enough to be fontified as such
+             "self" "True" "False")
         symbol-end)
     ;; Definitions
     (,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_))))
@@ -160,7 +163,7 @@ Used for syntactic keywords.  N is the match number (1, 2 or 3)."
               (= (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)))
+      (unless (nth 3 (syntax-ppss))
         (eval-when-compile (string-to-syntax "|"))))
      ;; Otherwise (we're in a non-matching string) the property is
      ;; nil, which is OK.
@@ -345,7 +348,7 @@ comments and strings, or that point is within brackets/parens."
                    (error nil))))))))
 
 (defun python-comment-line-p ()
-  "Return non-nil iff current line has only a comment."
+  "Return non-nil if current line has only a comment."
   (save-excursion
     (end-of-line)
     (when (eq 'comment (syntax-ppss-context (syntax-ppss)))
@@ -353,7 +356,7 @@ comments and strings, or that point is within brackets/parens."
       (looking-at (rx (or (syntax comment-start) line-end))))))
 
 (defun python-blank-line-p ()
-  "Return non-nil iff current line is blank."
+  "Return non-nil if current line is blank."
   (save-excursion
     (beginning-of-line)
     (looking-at "\\s-*$")))
@@ -371,7 +374,7 @@ BOS non-nil means point is known to be at beginning of statement."
   (save-excursion
     (unless bos (python-beginning-of-statement))
     (looking-at (rx (and (or "if" "else" "elif" "while" "for" "def"
-                            "class" "try" "except" "finally")
+                            "class" "try" "except" "finally" "with")
                         symbol-end)))))
 
 (defun python-close-block-statement-p (&optional bos)
@@ -405,6 +408,7 @@ The criteria are that the line isn't a comment or in string and
 See also `\\[python-guess-indent]'"
   :group 'python
   :type 'integer)
+(put 'python-indent 'safe-local-variable 'integerp)
 
 (defcustom python-guess-indent t
   "Non-nil means Python mode guesses `python-indent' for the buffer."
@@ -457,7 +461,7 @@ Set `python-indent' locally to the value guessed."
              (let ((initial (current-indentation)))
                (if (zerop (python-next-statement))
                    (setq indent (- (current-indentation) initial)))
-               (if (and (>= indent 2) (<= indent 8)) ; sanity check
+               (if (and indent (>= indent 2) (<= indent 8)) ; sanity check
                    (setq done t))))))
        (when done
          (when (/= indent (default-value 'python-indent))
@@ -846,7 +850,7 @@ multi-line bracketed expressions."
   "Skip out of any nested brackets.
 Skip forward if FORWARD is non-nil, else backward.
 If SYNTAX is non-nil it is the state returned by `syntax-ppss' at point.
-Return non-nil iff skipping was done."
+Return non-nil if skipping was done."
   (let ((depth (syntax-ppss-depth (or syntax (syntax-ppss))))
        (forward (if forward -1 1)))
     (unless (zerop depth)
@@ -879,10 +883,13 @@ On a comment line, go to end of line."
                           nil)
                          ((eq 'string (syntax-ppss-context s))
                           ;; Go to start of string and skip it.
-                          (goto-char (nth 8 s))
-                          (condition-case () ; beware invalid syntax
-                              (progn (forward-sexp) t)
-                            (error (end-of-line))))
+                           (let ((pos (point)))
+                             (goto-char (nth 8 s))
+                             (condition-case () ; beware invalid syntax
+                                 (progn (forward-sexp) t)
+                               ;; If there's a mismatched string, make sure
+                               ;; we still overall move *forward*.
+                               (error (goto-char pos) (end-of-line)))))
                          ((python-skip-out t s))))
             (end-of-line))
           (unless comment
@@ -981,7 +988,7 @@ don't move and return nil.  Otherwise return t."
                  (if (and (zerop ci) (not open))
                      (not (goto-char point))
                    (catch 'done
-                     (while (zerop (python-next-statement))
+                      (while (zerop (python-next-statement))
                        (when (or (and open (<= (current-indentation) ci))
                                  (< (current-indentation) ci))
                          (python-skip-comments/blanks t)
@@ -989,7 +996,16 @@ don't move and return nil.  Otherwise return t."
                          (throw 'done t)))))))
       (setq arg (1- arg)))
     (zerop arg)))
-\f
+
+(defvar python-which-func-length-limit 40
+  "Non-strict length limit for `python-which-func' output.")
+
+(defun python-which-func ()
+  (let ((function-name (python-current-defun python-which-func-length-limit)))
+    (set-text-properties 0 (length function-name) nil function-name)
+    function-name))
+
+
 ;;;; Imenu.
 
 (defvar python-recursing)
@@ -1146,7 +1162,7 @@ modified by the user.  Additional arguments are added when the command
 is used by `run-python' et al.")
 
 (defvar python-buffer nil
-  "*The current python process buffer.
+  "*The current Python process buffer.
 
 Commands that send text from source buffers to Python processes have
 to choose a process to send to.  This is determined by buffer-local
@@ -1183,7 +1199,7 @@ local value.")
     (define-key map "\C-c\C-l" 'python-load-file)
     (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,
+    ;; Python process even at the prompt provided we have a normal prompt,
     ;; i.e. '>>> ' and not '... '.  See the comment before
     ;; python-send-region.  Fixme: uncomment these if we address that.
 
@@ -1191,6 +1207,15 @@ local value.")
     ;; (define-key map "\C-c\C-f" 'python-describe-symbol)
     map))
 
+(defvar inferior-python-mode-syntax-table
+  (let ((st (make-syntax-table python-mode-syntax-table)))
+    ;; Don't get confused by apostrophes in the process's output (e.g. if
+    ;; you execute "help(os)").
+    (modify-syntax-entry ?\' "." st)
+    ;; Maybe we should do the same for double quotes?
+    ;; (modify-syntax-entry ?\" "." st)
+    st))
+
 ;; 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?
@@ -1213,7 +1238,6 @@ For running multiple processes in multiple buffers, see `run-python' and
 
 \\{inferior-python-mode-map}"
   :group 'python
-  (set-syntax-table python-mode-syntax-table)
   (setq mode-line-process '(":%s"))
   (set (make-local-variable 'comint-input-filter) 'python-input-filter)
   (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter
@@ -1286,7 +1310,7 @@ Don't save anything for STR matching `inferior-python-filter-regexp'."
                     ;; Maybe we could be more selective here.
                     (if (zerop (length res))
                         (not (bolp))
-                      (string-match res ".\\'"))))
+                      (string-match ".\\'" res))))
            ;; The need for this seems to be system-dependent:
            ;; What is this all about, exactly?  --Stef
            ;; (if (and (eq ?. (aref s 0)))
@@ -1335,7 +1359,7 @@ buffer for a list of commands.)"
                (path (getenv "PYTHONPATH"))
                (process-environment    ; to import emacs.py
                 (cons (concat "PYTHONPATH=" data-directory
-                              (if path (concat ":" path)))
+                              (if path (concat path-separator path)))
                       process-environment)))
           (apply 'make-comint-in-buffer "Python"
                  (if new (generate-new-buffer "*Python*") "*Python*")
@@ -1371,11 +1395,11 @@ buffer for a list of commands.)"
 COMMAND should be a single statement."
   ;; (assert (not (string-match "\n" command)))
   ;; (let ((end (marker-position (process-mark (python-proc)))))
-    (with-current-buffer python-buffer (goto-char (point-max)))
+  (with-current-buffer (process-buffer (python-proc))
+    (goto-char (point-max))
     (compilation-forget-errors)
     (python-send-string command)
-    (with-current-buffer python-buffer
-      (setq compilation-last-buffer (current-buffer)))
+    (setq compilation-last-buffer (current-buffer)))
     ;; No idea what this is for but it breaks the call to
     ;; compilation-fake-loc in python-send-region.  -- Stef
     ;; Must wait until this has completed before re-setting variables below.
@@ -1424,11 +1448,13 @@ COMMAND should be a single statement."
   "Evaluate STRING in inferior Python process."
   (interactive "sPython command: ")
   (comint-send-string (python-proc) string)
-  (comint-send-string (python-proc)
-                      ;; If the string is single-line or if it ends with \n,
-                      ;; only add a single \n, otherwise add 2, so as to
-                      ;; make sure we terminate the multiline instruction.
-                      (if (string-match "\n.+\\'" string) "\n\n" "\n")))
+  (unless (string-match "\n\\'" string)
+    ;; Make sure the text is properly LF-terminated.
+    (comint-send-string (python-proc) "\n"))
+  (when (string-match "\n[ \t].*\n?\\'" string)
+    ;; If the string contains a final indented line, add a second newline so
+    ;; as to make sure we terminate the multiline instruction.
+    (comint-send-string (python-proc) "\n")))
 
 (defun python-send-buffer ()
   "Send the current buffer to the inferior Python process."
@@ -1503,9 +1529,9 @@ See variable `python-buffer'.  Starts a new process if necessary."
   ;; isn't one for `python-buffer'.
   (unless (comint-check-proc python-buffer)
     (run-python nil t))
-  (get-buffer-process (or (if (derived-mode-p 'inferior-python-mode)
-                              (current-buffer)
-                            python-buffer))))
+  (get-buffer-process (if (derived-mode-p 'inferior-python-mode)
+                          (current-buffer)
+                        python-buffer)))
 
 (defun python-set-proc ()
   "Set the default value of `python-buffer' to correspond to this buffer.
@@ -1729,12 +1755,11 @@ Otherwise, do nothing."
               (orig (point))
               (start (nth 8 syntax))
               end)
-         (cond ((eq t (nth 3 syntax))      ; in fenced string
-                (goto-char (nth 8 syntax)) ; string start
-                (condition-case ()         ; for unbalanced quotes
-                    (progn (forward-sexp)
-                           (setq end (point)))
-                  (error (setq end (point-max)))))
+         (cond ((eq t (nth 3 syntax))        ; in fenced string
+                (goto-char (nth 8 syntax))   ; string start
+                (setq end (condition-case () ; for unbalanced quotes
+                               (progn (forward-sexp) (point))
+                             (error (point-max)))))
                ((re-search-backward "\\s|\\s-*\\=" nil t) ; end of fenced
                                                           ; string
                 (forward-char)
@@ -1742,13 +1767,17 @@ Otherwise, do nothing."
                 (condition-case ()
                     (progn (backward-sexp)
                            (setq start (point)))
-                  (error nil))))
+                  (error (setq end nil)))))
          (when end
            (save-restriction
              (narrow-to-region start end)
              (goto-char orig)
-             (fill-paragraph justify))))))
-      t)
+              (let ((paragraph-separate
+                     ;; Make sure that fenced-string delimiters that stand
+                     ;; on their own line stay there.
+                     (concat "[ \t]*['\"]+[ \t]*$\\|" paragraph-separate)))
+                (fill-paragraph justify))))))
+      t))
 
 (defun python-shift-left (start end &optional count)
   "Shift lines in region COUNT (the prefix arg) columns to the left.
@@ -1794,22 +1823,34 @@ of current line."
   (1+ (/ (current-indentation) python-indent)))
 
 ;; Fixme: Consider top-level assignments, imports, &c.
-(defun python-current-defun ()
+(defun python-current-defun (&optional length-limit)
   "`add-log-current-defun-function' for Python."
   (save-excursion
     ;; Move up the tree of nested `class' and `def' blocks until we
     ;; get to zero indentation, accumulating the defined names.
-    (let ((start t)
-         accum)
-      (while (or start (> (current-indentation) 0))
-       (setq start nil)
-       (python-beginning-of-block)
-       (end-of-line)
-       (beginning-of-defun)
-       (if (looking-at (rx (0+ space) (or "def" "class") (1+ space)
-                           (group (1+ (or word (syntax symbol))))))
-           (push (match-string 1) accum)))
-      (if accum (mapconcat 'identity accum ".")))))
+    (let ((accum)
+         (length -1))
+      (catch 'done
+       (while (or (null length-limit)
+                  (null (cdr accum))
+                  (< length length-limit))
+         (setq start nil)
+         (let ((started-from (point)))
+           (python-beginning-of-block)
+           (end-of-line)
+           (beginning-of-defun)
+           (when (= (point) started-from)
+             (throw 'done nil)))
+         (when (looking-at (rx (0+ space) (or "def" "class") (1+ space)
+                               (group (1+ (or word (syntax symbol))))))
+           (push (match-string 1) accum)
+           (setq length (+ length 1 (length (car accum)))))
+         (when (= (current-indentation) 0)
+           (throw 'done nil))))
+      (when accum
+       (when (and length-limit (> length length-limit))
+         (setcar accum ".."))
+       (mapconcat 'identity accum ".")))))
 
 (defun python-mark-block ()
   "Mark the block around point.
@@ -1905,7 +1946,7 @@ Repeating the command scrolls the completion window."
   (interactive)
   (let ((window (get-buffer-window "*Completions*")))
     (if (and (eq last-command this-command)
-            window (window-live-p window) (window-buffer window)
+            (window-live-p window) (window-buffer window)
             (buffer-name (window-buffer window)))
        (with-current-buffer (window-buffer window)
          (if (pos-visible-in-window-p (point-max) window)
@@ -1964,6 +2005,13 @@ Repeating the command scrolls the completion window."
 \f
 ;;;; Skeletons
 
+(defcustom python-use-skeletons nil
+  "Non-nil means template skeletons will be automagically inserted.
+This happens when pressing \"if<SPACE>\", for example, to prompt for
+the if condition."
+  :type 'boolean
+  :group 'python)
+
 (defvar python-skeletons nil
   "Alist of named skeletons for Python mode.
 Elements are of the form (NAME . EXPANDER-FUNCTION).")
@@ -1981,7 +2029,8 @@ The default contents correspond to the elements of `python-skeletons'.")
         (function (intern (concat "python-insert-" name))))
     `(progn
        (add-to-list 'python-skeletons ',(cons name function))
-       (define-abbrev python-mode-abbrev-table ,name "" ',function nil t)
+       (if python-use-skeletons
+          (define-abbrev python-mode-abbrev-table ,name "" ',function nil t))
        (define-skeleton ,function
         ,(format "Insert Python \"%s\" template." name)
         ,@elements)))))
@@ -2063,7 +2112,7 @@ The default contents correspond to the elements of `python-skeletons'.")
   > _ \n)
 
 (defvar python-default-template "if"
-  "Default template to expand by `python-insert-template'.
+  "Default template to expand by `python-expand-template'.
 Updated on each expansion.")
 
 (defun python-expand-template (name)
@@ -2200,6 +2249,7 @@ with skeleton expansions for compound statement templates.
                                   ;;  . python-font-lock-syntactic-face-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) "# ")
   (set (make-local-variable 'indent-line-function) #'python-indent-line)
   (set (make-local-variable 'indent-region-function) #'python-indent-region)
@@ -2210,7 +2260,7 @@ with skeleton expansions for compound statement templates.
        #'python-current-defun)
   (set (make-local-variable 'outline-regexp)
        (rx (* space) (or "class" "def" "elif" "else" "except" "finally"
-                        "for" "if" "try" "while")
+                        "for" "if" "try" "while" "with")
           symbol-end))
   (set (make-local-variable 'outline-heading-end-regexp) ":\\s-*\n")
   (set (make-local-variable 'outline-level) #'python-outline-level)
@@ -2219,6 +2269,7 @@ with skeleton expansions for compound statement templates.
   (set (make-local-variable 'beginning-of-defun-function)
        'python-beginning-of-defun)
   (set (make-local-variable 'end-of-defun-function) 'python-end-of-defun)
+  (add-hook 'which-func-functions 'python-which-func nil t)
   (setq imenu-create-index-function #'python-imenu-create-index)
   (set (make-local-variable 'eldoc-documentation-function)
        #'python-eldoc-function)