mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 17:24:23 +00:00
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.
This commit is contained in:
parent
e21766aa63
commit
2bcda1d5ac
1 changed files with 355 additions and 237 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue