aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorChong Yidong <[email protected]>2008-02-21 17:03:47 +0000
committerChong Yidong <[email protected]>2008-02-21 17:03:47 +0000
commit2bcda1d5ac8c029e29619eedd94ccff4ea91a310 (patch)
tree873ea5c447c9ccd0f9c29cff8b2507c00303dbd0 /lisp/progmodes/python.el
parente21766aa6370c6bcd5c1c5f1f7de972408e1e971 (diff)
Merge from Dave Love's 2008-01-20 version.
Require sym-comp. Add Python buffer to same-window-buffer-names. Fixup whitespaces. (python-font-lock-keywords): Add highlighting for Python builtins. (python-font-lock-syntactic-keywords): Rewrite. (python-quote-syntax): Use syntax-ppss-context instead of parsing ppss directly. (python-mode-map): Add binding for python-find-function. (python-calculate-indentation): Clean up the logic. (python-beginning-of-defun): Explicitly set return value. (python-beginning-of-statement): Stop looping if we get stuck going backwards. (python-next-statement): Stop looping if we somehow end up inside a string while advancing. (python-preoutput-continuation, python-version-checked): New vars. (python-check-version): New function. (run-python): Set default command to python-command instead of python-python-command. (run-python): Use python-check-version. Give PYTHONPATH precedence over data-directory in the process environment. Load function definitions in python process after. (python-check-comint-prompt): New function. (python-send-command, python-send-receive): Use it. (python-complete-symbol, python-try-complete): Functions deleted. Use symbol-complete instead of python-complete-symbol throughout. (python-fill-paragraph): Further refine the fenced-string regexp. (def-python-skeleton): Expand to the original abbrev instead if in a comment or string. Tweak skeletons for `if', `while', `for', `try/except', `try/finally', `name'. (python-pea-hook, python-abbrev-pc-hook): New functions. (python-abbrev-syntax-table): New var. (python-mode): Add python-pea-hook to pre-abbrev-expand-hook. Use symbol-completion-try-complete for hippie expansion. Turn on font lock unconditionally. (python-mode-hook): Defcustom it. No need to use make-local variable on indent-tabs-mode in "Turn off Indent Tabs mode" option, since it's buffer-local.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el592
1 files changed, 355 insertions, 237 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 11662c8e46..9057e51e43 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -62,6 +62,8 @@
;; TODO: See various Fixmes below.
+;; Fixme: This doesn't support (the nascent) Python 3 .
+
;;; Code:
(require 'comint)
@@ -70,6 +72,7 @@
(require 'compile)
(require 'hippie-exp))
+(require 'sym-comp)
(autoload 'comint-mode "comint")
(defgroup python nil
@@ -84,6 +87,7 @@
(add-to-list 'interpreter-mode-alist '("python" . python-mode))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
+(add-to-list 'same-window-buffer-names "*Python*")
;;;; Font lock
@@ -98,7 +102,7 @@
;; 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.5 manual
. font-lock-constant-face)
;; Definitions
(,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_))))
@@ -108,15 +112,63 @@
;; Top-level assignments are worth highlighting.
(,(rx line-start (group (1+ (or word ?_))) (0+ space) "=")
(1 font-lock-variable-name-face))
- (,(rx "@" (1+ (or word ?_))) ; decorators
- (0 font-lock-preprocessor-face))))
+ (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)))) ; decorators
+ (1 font-lock-type-face))
+ ;; Built-ins. (The next three blocks are from
+ ;; `__builtin__.__dict__.keys()' in Python 2.5.1.) These patterns
+ ;; are debateable, but they at least help to spot possible
+ ;; shadowing of builtins.
+ (,(rx symbol-start (or
+ ;; exceptions
+ "ArithmeticError" "AssertionError" "AttributeError"
+ "BaseException" "DeprecationWarning" "EOFError"
+ "EnvironmentError" "Exception" "FloatingPointError"
+ "FutureWarning" "GeneratorExit" "IOError" "ImportError"
+ "ImportWarning" "IndentationError" "IndexError" "KeyError"
+ "KeyboardInterrupt" "LookupError" "MemoryError" "NameError"
+ "NotImplemented" "NotImplementedError" "OSError"
+ "OverflowError" "PendingDeprecationWarning" "ReferenceError"
+ "RuntimeError" "RuntimeWarning" "StandardError"
+ "StopIteration" "SyntaxError" "SyntaxWarning" "SystemError"
+ "SystemExit" "TabError" "TypeError" "UnboundLocalError"
+ "UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError"
+ "UnicodeTranslateError" "UnicodeWarning" "UserWarning"
+ "ValueError" "Warning" "ZeroDivisionError") symbol-end)
+ . font-lock-type-face)
+ (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start
+ (group (or
+ ;; callable built-ins, fontified when not appearing as
+ ;; object attributes
+ "abs" "all" "any" "apply" "basestring" "bool" "buffer" "callable"
+ "chr" "classmethod" "cmp" "coerce" "compile" "complex"
+ "copyright" "credits" "delattr" "dict" "dir" "divmod"
+ "enumerate" "eval" "execfile" "exit" "file" "filter" "float"
+ "frozenset" "getattr" "globals" "hasattr" "hash" "help"
+ "hex" "id" "input" "int" "intern" "isinstance" "issubclass"
+ "iter" "len" "license" "list" "locals" "long" "map" "max"
+ "min" "object" "oct" "open" "ord" "pow" "property" "quit"
+ "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)
+ (1 font-lock-builtin-face))
+ (,(rx symbol-start (or
+ ;; other built-ins
+ "True" "False" "None" "Ellipsis"
+ "_" "__debug__" "__doc__" "__import__" "__name__") symbol-end)
+ . font-lock-builtin-face)))
(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 (or line-start buffer-start
- (not (syntax escape))) ; avoid escaped leading quote
- (group (optional (any "uUrR"))) ; prefix gets syntax property
+ ;; 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
@@ -144,8 +196,6 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)."
;; x = ''' """ ' a
;; '''
;; x '"""' x """ \"""" x
- ;; Fixme: """""" goes wrong (due to syntax-ppss not getting the string
- ;; fence context).
(save-excursion
(goto-char (match-beginning 0))
(cond
@@ -165,8 +215,8 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)."
(and (= n 1) ; prefix
(/= (match-beginning 1) (match-end 1)))) ; non-empty
(let ((font-lock-syntactic-keywords nil))
- (unless (nth 3 (syntax-ppss))
- (eval-when-compile (string-to-syntax "|")))))
+ (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.
)))
@@ -217,7 +267,7 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)."
(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 'python-complete-symbol
+ (substitute-key-definition 'complete-symbol 'symbol-complete
map global-map)
(define-key map "\C-c\C-i" 'python-find-imports)
(define-key map "\C-c\C-t" 'python-expand-template)
@@ -270,8 +320,10 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)."
"-"
["Help on symbol" python-describe-symbol
:help "Use pydoc on symbol at point"]
- ["Complete symbol" python-complete-symbol
+ ["Complete symbol" symbol-complete
:help "Complete (qualified) symbol before point"]
+ ["Find function" python-find-function
+ :help "Try to find source definition of function at point"]
["Update imports" python-find-imports
:help "Update list of top-level imports for completion"]))
map))
@@ -630,39 +682,38 @@ Set `python-indent' locally to the value guessed."
((looking-at (rx (0+ space) (syntax comment-start)
(not (any " \t\n")))) ; non-indentable comment
(current-indentation))
- (t (if python-honour-comment-indentation
- ;; Back over whitespace, newlines, non-indentable comments.
- (catch 'done
- (while t
- (if (cond ((bobp))
- ;; not at comment start
- ((not (forward-comment -1))
- (python-beginning-of-statement)
- t)
- ;; trailing comment
- ((/= (current-column) (current-indentation))
- (python-beginning-of-statement)
- t)
- ;; indentable comment like python-mode.el
- ((and (looking-at (rx (syntax comment-start)
- (or space line-end)))
- (/= 0 (current-column)))))
- (throw 'done t)))))
- (python-indentation-levels)
- ;; Prefer to indent comments with an immediately-following
- ;; statement, e.g.
- ;; ...
- ;; # ...
- ;; def ...
- (when (and (> python-indent-list-length 1)
- (python-comment-line-p))
- (forward-line)
- (unless (python-comment-line-p)
- (let ((elt (assq (current-indentation) python-indent-list)))
- (setq python-indent-list
- (nconc (delete elt python-indent-list)
- (list elt))))))
- (caar (last python-indent-list)))))))
+ ((and python-honour-comment-indentation
+ ;; Back over whitespace, newlines, non-indentable comments.
+ (catch 'done
+ (while (cond ((bobp) nil)
+ ((not (forward-comment -1))
+ nil) ; not at comment start
+ ;; Now at start of comment -- trailing one?
+ ((/= (current-column) (current-indentation))
+ nil)
+ ;; Indentable comment, like python-mode.el?
+ ((and (looking-at (rx (syntax comment-start)
+ (or space line-end)))
+ (/= 0 (current-column)))
+ (throw 'done (current-column)))
+ ;; Else skip it (loop).
+ (t))))))
+ (t
+ (python-indentation-levels)
+ ;; Prefer to indent comments with an immediately-following
+ ;; statement, e.g.
+ ;; ...
+ ;; # ...
+ ;; def ...
+ (when (and (> python-indent-list-length 1)
+ (python-comment-line-p))
+ (forward-line)
+ (unless (python-comment-line-p)
+ (let ((elt (assq (current-indentation) python-indent-list)))
+ (setq python-indent-list
+ (nconc (delete elt python-indent-list)
+ (list elt))))))
+ (caar (last python-indent-list)))))))
;;;; Cycling through the possible indentations with successive TABs.
@@ -855,11 +906,12 @@ reached start of buffer."
;; Not sure why it was like this -- fails in case of
;; last internal function followed by first
;; non-def statement of the main body.
- ;;(and def-line (= in ci))
+;; (and def-line (= in ci))
(= in ci)
(< in ci)))
(not (python-in-string/comment)))
- (setq found t)))))
+ (setq found t)))
+ found))
(defun python-end-of-defun ()
"`end-of-defun-function' for Python.
@@ -913,15 +965,17 @@ Accounts for continuation lines, multi-line strings, and
multi-line bracketed expressions."
(beginning-of-line)
(python-beginning-of-string)
- (while (python-continuation-line-p)
- (beginning-of-line)
- (if (python-backslash-continuation-line-p)
- (progn
- (forward-line -1)
- (while (python-backslash-continuation-line-p)
- (forward-line -1)))
- (python-beginning-of-string)
- (python-skip-out)))
+ (let ((point (point)))
+ (while (and (python-continuation-line-p)
+ (> point (setq point (point))))
+ (beginning-of-line)
+ (if (python-backslash-continuation-line-p)
+ (progn
+ (forward-line -1)
+ (while (python-backslash-continuation-line-p)
+ (forward-line -1)))
+ (python-beginning-of-string)
+ (python-skip-out))))
(back-to-indentation))
(defun python-skip-out (&optional forward syntax)
@@ -999,11 +1053,14 @@ Return count of statements left to move."
(if (< count 0)
(python-previous-statement (- count))
(beginning-of-line)
- (while (and (> count 0) (not (eobp)))
- (python-end-of-statement)
- (python-skip-comments/blanks)
- (unless (eobp)
- (setq count (1- count))))
+ (let (bogus)
+ (while (and (> count 0) (not (eobp)) (not bogus))
+ (python-end-of-statement)
+ (python-skip-comments/blanks)
+ (if (eq 'string (syntax-ppss-context (syntax-ppss)))
+ (setq bogus t)
+ (unless (eobp)
+ (setq count (1- count))))))
count))
(defun python-beginning-of-block (&optional arg)
@@ -1066,7 +1123,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)
@@ -1083,9 +1140,21 @@ don't move and return nil. Otherwise return t."
(set-text-properties 0 (length function-name) nil function-name)
function-name))
-
+
;;;; Imenu.
+;; For possibily speeding this up, here's the top of the ELP profile
+;; for rescanning pydoc.py (2.2k lines, 90kb):
+;; Function Name Call Count Elapsed Time Average Time
+;; ==================================== ========== ============= ============
+;; python-imenu-create-index 156 2.430906 0.0155827307
+;; python-end-of-defun 155 1.2718260000 0.0082053290
+;; python-end-of-block 155 1.1898689999 0.0076765741
+;; python-next-statement 2970 1.024717 0.0003450225
+;; python-end-of-statement 2970 0.4332190000 0.0001458649
+;; python-beginning-of-defun 265 0.0918479999 0.0003465962
+;; python-skip-comments/blanks 3125 0.0753319999 2.410...e-05
+
(defvar python-recursing)
(defun python-imenu-create-index ()
"`imenu-create-index-function' for Python.
@@ -1361,11 +1430,16 @@ Don't save anything for STR matching `inferior-python-filter-regexp'."
(defvar python-preoutput-result nil
"Data from last `_emacs_out' line seen by the preoutput filter.")
+(defvar python-preoutput-continuation nil
+ "If non-nil, funcall this when `python-preoutput-filter' sees `_emacs_ok'.")
+
(defvar python-preoutput-leftover nil)
(defvar python-preoutput-skip-next-prompt 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
@@ -1415,6 +1489,23 @@ Don't save anything for STR matching `inferior-python-filter-regexp'."
(autoload 'comint-check-proc "comint")
+(defvar python-version-checked nil)
+(defun python-check-version (cmd)
+ "Check that CMD runs a suitable version of Python."
+ ;; Fixme: Check on Jython.
+ (unless (or python-version-checked
+ (equal 0 (string-match (regexp-quote python-python-command)
+ 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")))
+ (setq python-version-checked t)))
+
;;;###autoload
(defun run-python (&optional cmd noshow new)
"Run an inferior Python process, input and output via buffer *Python*.
@@ -1435,22 +1526,26 @@ buffer for a list of commands.)"
(interactive (if current-prefix-arg
(list (read-string "Run Python: " python-command) nil t)
(list python-command)))
- (unless cmd (setq cmd python-python-command))
+ (unless cmd (setq cmd python-command))
+ (python-check-version cmd)
(setq python-command cmd)
;; Fixme: Consider making `python-buffer' buffer-local as a buffer
;; (not a name) in Python buffers from which `run-python' &c is
;; invoked. Would support multiple processes better.
(when (or new (not (comint-check-proc python-buffer)))
(with-current-buffer
- (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-separator path)))
- process-environment)))
- (apply 'make-comint-in-buffer "Python"
- (if new (generate-new-buffer "*Python*") "*Python*")
- (car cmdlist) nil (cdr cmdlist)))
+ (let* ((cmdlist (append (python-args-to-list cmd) '("-i")))
+ (path (getenv "PYTHONPATH"))
+ (process-environment ; to import emacs.py
+ (cons (concat "PYTHONPATH="
+ (if path (concat path path-separator))
+ data-directory)
+ process-environment))
+ ;; Suppress use of pager for help output:
+ (process-connection-type nil))
+ (apply 'make-comint-in-buffer "Python"
+ (generate-new-buffer "*Python*")
+ (car cmdlist) nil (cdr cmdlist)))
(setq-default python-buffer (current-buffer))
(setq python-buffer (current-buffer))
(accept-process-output (get-buffer-process python-buffer) 5)
@@ -1462,7 +1557,13 @@ buffer for a list of commands.)"
;; seems worth putting in a separate file, and it's probably cleaner
;; to put it in a module.
;; Ensure we're at a prompt before doing anything else.
- (python-send-string "import emacs")))
+ (python-send-string "import emacs")
+ ;; The following line was meant to ensure that we're at a prompt
+ ;; before doing anything else. However, this can cause Emacs to
+ ;; hang waiting for a response, if that Python function fails
+ ;; (i.e. raises an exception).
+ ;; (python-send-receive "print '_emacs_out ()'")
+ ))
(if (derived-mode-p 'python-mode)
(setq python-buffer (default-value 'python-buffer))) ; buffer-local
;; Without this, help output goes into the inferior python buffer if
@@ -1470,30 +1571,14 @@ buffer for a list of commands.)"
(sit-for 1 t) ;Should we use accept-process-output instead? --Stef
(unless noshow (pop-to-buffer python-buffer t)))
-;; Fixme: We typically lose if the inferior isn't in the normal REPL,
-;; e.g. prompt is `help> '. Probably raise an error if the form of
-;; the prompt is unexpected. Actually, it needs to be `>>> ', not
-;; `... ', i.e. we're not inputting a block &c. However, this may not
-;; be the place to check it, e.g. we might actually want to send
-;; commands having set up such a state.
-
(defun python-send-command (command)
- "Like `python-send-string' but resets `compilation-shell-minor-mode'.
-COMMAND should be a single statement."
- ;; (assert (not (string-match "\n" command)))
- ;; (let ((end (marker-position (process-mark (python-proc)))))
- (with-current-buffer (process-buffer (python-proc))
- (goto-char (point-max))
- (compilation-forget-errors)
- (python-send-string command)
- (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.
- ;; (python-send-receive "print '_emacs_out ()'")
- ;; (with-current-buffer python-buffer
- ;; (set-marker compilation-parsing-end end))
- ) ;;)
+ "Like `python-send-string' but resets `compilation-shell-minor-mode'."
+ (when (python-check-comint-prompt)
+ (with-current-buffer (process-buffer (python-proc))
+ (goto-char (point-max))
+ (compilation-forget-errors)
+ (python-send-string command)
+ (setq compilation-last-buffer (current-buffer)))))
(defun python-send-region (start end)
"Send the region to the inferior Python process."
@@ -1617,8 +1702,8 @@ See variable `python-buffer'. Starts a new process if necessary."
(unless (comint-check-proc python-buffer)
(run-python nil t))
(get-buffer-process (if (derived-mode-p 'inferior-python-mode)
- (current-buffer)
- python-buffer)))
+ (current-buffer)
+ python-buffer)))
(defun python-set-proc ()
"Set the default value of `python-buffer' to correspond to this buffer.
@@ -1693,16 +1778,27 @@ will."
(defun python-send-receive (string)
"Send STRING to inferior Python (if any) and return result.
-The result is what follows `_emacs_out' in the output."
+The result is what follows `_emacs_out' in the output.
+This is a no-op if `python-check-comint-prompt' returns nil."
(python-send-string string)
(let ((proc (python-proc)))
(with-current-buffer (process-buffer proc)
- (set (make-local-variable 'python-preoutput-result) nil)
- (while (progn
- (accept-process-output proc 5)
- (null python-preoutput-result)))
- (prog1 python-preoutput-result
- (kill-local-variable 'python-preoutput-result)))))
+ (when (python-check-comint-prompt proc)
+ (set (make-local-variable 'python-preoutput-result) nil)
+ (while (progn
+ (accept-process-output proc 5)
+ (null python-preoutput-result)))
+ (prog1 python-preoutput-result
+ (kill-local-variable 'python-preoutput-result))))))
+
+(defun python-check-comint-prompt (&optional proc)
+ "Return non-nil iff there's a normal prompt in the inferior buffer.
+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)))))
;; Fixme: Is there anything reasonable we can do with random methods?
;; (Currently only works with functions.)
@@ -1716,22 +1812,22 @@ instance. Assumes an inferior Python is running."
(with-local-quit
;; First try the symbol we're on.
(or (and symbol
- (python-send-receive (format "emacs.eargs(%S, %s)"
- symbol python-imports)))
- ;; Try moving to symbol before enclosing parens.
- (let ((s (syntax-ppss)))
- (unless (zerop (car s))
- (when (eq ?\( (char-after (nth 1 s)))
- (save-excursion
- (goto-char (nth 1 s))
- (skip-syntax-backward "-")
- (let ((point (point)))
- (skip-chars-backward "a-zA-Z._")
- (if (< (point) point)
- (python-send-receive
- (format "emacs.eargs(%S, %s)"
- (buffer-substring-no-properties (point) point)
- python-imports))))))))))))
+ (python-send-receive (format "emacs.eargs(%S, %s)"
+ symbol python-imports)))
+ ;; Try moving to symbol before enclosing parens.
+ (let ((s (syntax-ppss)))
+ (unless (zerop (car s))
+ (when (eq ?\( (char-after (nth 1 s)))
+ (save-excursion
+ (goto-char (nth 1 s))
+ (skip-syntax-backward "-")
+ (let ((point (point)))
+ (skip-chars-backward "a-zA-Z._")
+ (if (< (point) point)
+ (python-send-receive
+ (format "emacs.eargs(%S, %s)"
+ (buffer-substring-no-properties (point) point)
+ python-imports))))))))))))
;;;; Info-look functionality.
@@ -1751,9 +1847,9 @@ Used with `eval-after-load'."
(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))
- t)
+ (Info-goto-node (format "(python%s-lib)Miscellaneous Index"
+ version))
+ t)
(error nil)))))
(info-lookup-maybe-add-help
:mode 'python-mode
@@ -1829,7 +1925,7 @@ The criterion is either a match for `jython-mode' via
"`fill-paragraph-function' handling multi-line strings and possibly comments.
If any of the current line is in or at the end of a multi-line string,
fill the string or the paragraph of it that point is in, preserving
-the strings's indentation."
+the string's indentation."
(interactive "P")
(or (fill-comment-paragraph justify)
(save-excursion
@@ -1845,7 +1941,7 @@ the strings's indentation."
;; paragraph in a multi-line string properly, so narrow
;; to the string and then fill around (the end of) the
;; current line.
- ((eq t (nth 3 syntax)) ; in fenced string
+ ((eq t (nth 3 syntax)) ; in fenced string
(goto-char (nth 8 syntax)) ; string start
(setq start (line-beginning-position))
(setq end (condition-case () ; for unbalanced quotes
@@ -1868,14 +1964,25 @@ the strings's indentation."
;; """
;; ...
;; """
- (let* ((paragraph-separate
- (concat ".*\\s|\"\"$" ; newline after opening quotes
- "\\|\\(?:" paragraph-separate "\\)"))
- (paragraph-start
- (concat ".*\\s|\"\"[ \t]*[^ \t].*" ; not newline after
- ; opening quotes
- "\\|\\(?:" paragraph-separate "\\)"))
- (fill-paragraph-function))
+ (let ((paragraph-separate
+ ;; Note that the string could be part of an
+ ;; expression, so it can have preceding and
+ ;; trailing non-whitespace.
+ (concat
+ (rx (or
+ ;; Opening triple quote without following text.
+ (and (* nonl)
+ (group (syntax string-delimiter))
+ (repeat 2 (backref 1))
+ ;; Fixme: Not sure about including
+ ;; trailing whitespace.
+ (* (any " \t"))
+ eol)
+ ;; Closing trailing quote without preceding text.
+ (and (group (any ?\" ?')) (backref 2)
+ (syntax string-delimiter))))
+ "\\(?:" paragraph-separate "\\)"))
+ fill-paragraph-function)
(fill-paragraph justify))))))) t)
(defun python-shift-left (start end &optional count)
@@ -1988,7 +2095,8 @@ Uses `python-beginning-of-block', `python-end-of-block'."
(let ((start (line-beginning-position)))
;; Skip over continued lines.
(while (and (eq ?\\ (char-before (line-end-position)))
- (= 0 (forward-line 1))))
+ (= 0 (forward-line 1)))
+ t)
(push (buffer-substring start (line-beginning-position 2))
lines))))
(setq python-imports
@@ -2041,60 +2149,6 @@ Uses `python-imports' to load modules against which to complete."
nil t)
(match-beginning 1)))))
(if start (buffer-substring-no-properties start end))))
-
-(defun python-complete-symbol ()
- "Perform completion on the Python symbol preceding point.
-Repeating the command scrolls the completion window."
- (interactive)
- (let ((window (get-buffer-window "*Completions*")))
- (if (and (eq last-command this-command)
- (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)
- (set-window-start window (point-min))
- (save-selected-window
- (select-window window)
- (scroll-up))))
- ;; Do completion.
- (let* ((end (point))
- (symbol (python-partial-symbol))
- (completions (python-symbol-completions symbol))
- (completion (if completions
- (try-completion symbol completions))))
- (when symbol
- (cond ((eq completion t))
- ((null completion)
- (message "Can't find completion for \"%s\"" symbol)
- (ding))
- ((not (string= symbol completion))
- (delete-region (- end (length symbol)) end)
- (insert completion))
- (t
- (message "Making completion list...")
- (with-output-to-temp-buffer "*Completions*"
- (display-completion-list completions symbol))
- (message "Making completion list...%s" "done"))))))))
-
-(defun python-try-complete (old)
- "Completion function for Python for use with `hippie-expand'."
- (when (derived-mode-p 'python-mode) ; though we only add it locally
- (unless old
- (let ((symbol (python-partial-symbol)))
- (he-init-string (- (point) (length symbol)) (point))
- (if (not (he-string-member he-search-string he-tried-table))
- (push he-search-string he-tried-table))
- (setq he-expand-list
- (and symbol (python-symbol-completions symbol)))))
- (while (and he-expand-list
- (he-string-member (car he-expand-list) he-tried-table))
- (pop he-expand-list))
- (if he-expand-list
- (progn
- (he-substitute-string (pop he-expand-list))
- t)
- (if old (he-reset-string))
- nil)))
;;;; FFAP support
@@ -2105,6 +2159,33 @@ Repeating the command scrolls the completion window."
(eval-after-load "ffap"
'(push '(python-mode . python-module-path) ffap-alist))
+;;;; Find-function support
+
+;; Fixme: key binding?
+
+(defun python-find-function (name)
+ "Find source of definition of function NAME.
+Interactively, prompt for name."
+ (interactive
+ (let ((symbol (with-syntax-table python-dotty-syntax-table
+ (current-word)))
+ (enable-recursive-minibuffers t))
+ (list (read-string (if symbol
+ (format "Find location of (default %s): " symbol)
+ "Find location of: ")
+ nil nil symbol))))
+ (unless python-imports
+ (error "Not called from buffer visiting Python file"))
+ (let* ((loc (python-send-receive (format "emacs.location_of (%S, %s)"
+ name python-imports)))
+ (loc (car (read-from-string loc)))
+ (file (car loc))
+ (line (cdr loc)))
+ (unless file (error "Don't know where `%s' is defined" name))
+ (pop-to-buffer (find-file-noselect file))
+ (when (integerp line)
+ (goto-line line))))
+
;;;; Skeletons
(defcustom python-use-skeletons nil
@@ -2132,24 +2213,31 @@ 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
- :system t :case-fixed t
- :enable-function (lambda () python-use-skeletons))
+ ;; Usual technique for inserting a skeleton, but expand
+ ;; to the original abbrev instead if in a comment or string.
+ (define-abbrev python-mode-abbrev-table ,name ""
+ ;; Quote this to give a readable abbrev table.
+ '(lambda ()
+ (if (python-in-string/comment)
+ (insert ,name)
+ (,function)))
+ nil t) ; system abbrev
(define-skeleton ,function
,(format "Insert Python \"%s\" template." name)
,@elements)))))
(put 'def-python-skeleton 'lisp-indent-function 2)
-;; From `skeleton-further-elements':
+;; From `skeleton-further-elements' set below:
;; `<': outdent a level;
;; `^': delete indentation on current line and also previous newline.
-;; Not quote like `delete-indentation'. Assumes point is at
+;; Not quite like `delete-indentation'. Assumes point is at
;; beginning of indentation.
(def-python-skeleton if
"Condition: "
"if " str ":" \n
- > _ \n
+ > -1 ; Fixme: I don't understand the spurious space this removes.
+ _ \n
("other condition, %s: "
< ; Avoid wrong indentation after block opening.
"elif " str ":" \n
@@ -2167,19 +2255,19 @@ The default contents correspond to the elements of `python-skeletons'."
(def-python-skeleton while
"Condition: "
"while " str ":" \n
- > _ \n
+ > -1 _ \n
'(python-else) | ^)
(def-python-skeleton for
"Target, %s: "
"for " str " in " (skeleton-read "Expression, %s: ") ":" \n
- > _ \n
+ > -1 _ \n
'(python-else) | ^)
(def-python-skeleton try/except
nil
"try:" \n
- > _ \n
+ > -1 _ \n
("Exception, %s: "
< "except " str '(python-target) ":" \n
> _ \n nil)
@@ -2194,7 +2282,7 @@ The default contents correspond to the elements of `python-skeletons'."
(def-python-skeleton try/finally
nil
"try:" \n
- > _ \n
+ > -1 _ \n
< "finally:" \n
> _ \n)
@@ -2202,7 +2290,7 @@ The default contents correspond to the elements of `python-skeletons'."
"Name: "
"def " str " (" ("Parameter, %s: " (unless (equal ?\( (char-before)) ", ")
str) "):" \n
- "\"\"\"" @ " \"\"\"" \n ; Fixme: syntaxification wrong for """"""
+ "\"\"\"" - "\"\"\"" \n ; Fixme: extra space inserted -- why?).
> _ \n)
(def-python-skeleton class
@@ -2212,7 +2300,7 @@ The default contents correspond to the elements of `python-skeletons'."
str)
& ")" | -2 ; close list or remove opening
":" \n
- "\"\"\"" @ " \"\"\"" \n
+ "\"\"\"" - "\"\"\"" \n
> _ \n)
(defvar python-default-template "if"
@@ -2255,35 +2343,35 @@ without confirmation."
(pymacs-load "bikeemacs" "brm-") ; first line of normal recipe
(let ((py-mode-map (make-sparse-keymap)) ; it assumes this
(features (cons 'python-mode features))) ; and requires this
- (brm-init)) ; second line of normal recipe
- (remove-hook 'python-mode-hook ; undo this from `brm-init'
- '(lambda () (easy-menu-add brm-menu)))
- (easy-menu-define
- python-brm-menu python-mode-map
- "Bicycle Repair Man"
- '("BicycleRepairMan"
- :help "Interface to navigation and refactoring tool"
- "Queries"
- ["Find References" brm-find-references
- :help "Find references to name at point in compilation buffer"]
- ["Find Definition" brm-find-definition
- :help "Find definition of name at point"]
- "-"
- "Refactoring"
- ["Rename" brm-rename
- :help "Replace name at point with a new name everywhere"]
- ["Extract Method" brm-extract-method
- :active (and mark-active (not buffer-read-only))
- :help "Replace statements in region with a method"]
- ["Extract Local Variable" brm-extract-local-variable
- :active (and mark-active (not buffer-read-only))
- :help "Replace expression in region with an assignment"]
- ["Inline Local Variable" brm-inline-local-variable
- :help
- "Substitute uses of variable at point with its definition"]
- ;; Fixme: Should check for anything to revert.
- ["Undo Last Refactoring" brm-undo :help ""])))
- (error (error "Bicyclerepairman setup failed: %s" data))))
+ (brm-init) ; second line of normal recipe
+ (remove-hook 'python-mode-hook ; undo this from `brm-init'
+ '(lambda () (easy-menu-add brm-menu)))
+ (easy-menu-define
+ python-brm-menu python-mode-map
+ "Bicycle Repair Man"
+ '("BicycleRepairMan"
+ :help "Interface to navigation and refactoring tool"
+ "Queries"
+ ["Find References" brm-find-references
+ :help "Find references to name at point in compilation buffer"]
+ ["Find Definition" brm-find-definition
+ :help "Find definition of name at point"]
+ "-"
+ "Refactoring"
+ ["Rename" brm-rename
+ :help "Replace name at point with a new name everywhere"]
+ ["Extract Method" brm-extract-method
+ :active (and mark-active (not buffer-read-only))
+ :help "Replace statements in region with a method"]
+ ["Extract Local Variable" brm-extract-local-variable
+ :active (and mark-active (not buffer-read-only))
+ :help "Replace expression in region with an assignment"]
+ ["Inline Local Variable" brm-inline-local-variable
+ :help
+ "Substitute uses of variable at point with its definition"]
+ ;; Fixme: Should check for anything to revert.
+ ["Undo Last Refactoring" brm-undo :help ""]))))
+ (error (error "BicycleRepairMan setup failed: %s" data))))
;;;; Modes.
@@ -2291,10 +2379,27 @@ without confirmation."
(defvar eldoc-documentation-function)
(defvar python-mode-running) ;Dynamically scoped var.
+;; Stuff to allow expanding abbrevs with non-word constituents.
+(defun python-abbrev-pc-hook ()
+ "Reset the syntax table after possibly expanding abbrevs."
+ (remove-hook 'post-command-hook 'python-abbrev-pc-hook t)
+ (set-syntax-table python-mode-syntax-table))
+
+(defvar python-abbrev-syntax-table
+ (copy-syntax-table python-mode-syntax-table)
+ "Syntax table used when expanding abbrevs.")
+
+(defun python-pea-hook ()
+ "Set the syntax table before possibly expanding abbrevs."
+ (set-syntax-table python-abbrev-syntax-table)
+ (add-hook 'post-command-hook 'python-abbrev-pc-hook nil t))
+(modify-syntax-entry ?/ "w" python-abbrev-syntax-table)
+
;;;###autoload
(define-derived-mode python-mode fundamental-mode "Python"
"Major mode for editing Python files.
-Font Lock mode is currently required for correct parsing of the source.
+Turns on Font Lock mode unconditionally since it is currently required
+for correct parsing of the source.
See also `jython-mode', which is actually invoked if the buffer appears to
contain Jython code. See also `run-python' and associated Python mode
commands for running Python under Emacs.
@@ -2363,24 +2468,31 @@ with skeleton expansions for compound statement templates.
(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)
;; 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.
(add-to-list 'hs-special-modes-alist
`(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#"
,(lambda (arg)
- (python-end-of-defun)
- (skip-chars-backward " \t\n"))
+ (python-end-of-defun)
+ (skip-chars-backward " \t\n"))
nil))
(set (make-local-variable 'skeleton-further-elements)
'((< '(backward-delete-char-untabify (min python-indent
(current-column))))
(^ '(- (1+ (current-indentation))))))
+ (add-hook 'pre-abbrev-expand-hook 'python-pea-hook nil t)
(if (featurep 'hippie-exp)
(set (make-local-variable 'hippie-expand-try-functions-list)
- (cons 'python-try-complete 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))
(when python-guess-indent (python-guess-indent))
;; Let's make it harder for the user to shoot himself in the foot.
(unless (= tab-width python-indent)
@@ -2391,11 +2503,16 @@ with skeleton expansions for compound statement templates.
(let ((python-mode-running t))
(python-maybe-jython))))
+;; Not done automatically in Emacs 21 or 22.
+(defcustom python-mode-hook nil
+ "Hook run when entering Python mode."
+ :group 'python
+ :type 'hook)
(custom-add-option 'python-mode-hook 'imenu-add-menubar-index)
(custom-add-option 'python-mode-hook
(lambda ()
- "Turn off Indent Tabs mode."
- (set (make-local-variable 'indent-tabs-mode) nil)))
+ "Turn off Indent Tabs mode."
+ (setq indent-tabs-mode nil)))
(custom-add-option 'python-mode-hook 'turn-on-eldoc-mode)
(custom-add-option 'python-mode-hook 'abbrev-mode)
(custom-add-option 'python-mode-hook 'python-setup-brm)
@@ -2776,5 +2893,6 @@ filter."
(provide 'python)
(provide 'python-21)
+
;; arch-tag: 6fce1d99-a704-4de9-ba19-c6e4912b0554
;;; python.el ends here