Doc fixes. Changes for compiler warnings.

(syntax): Don't require.
(python) <defgroup>: Add :version.
(python-quote-syntax): Re-written.
(inferior-python-mode): Move stuff here from run-python and add
some more.
(python-preoutput-continuation, python-preoutput-result)
(python-dotty-syntax-table): New.
(python-describe-symbol): Use them.
(run-python): Move stuff to inferior-python-mode.  Modify code
loaded into Python.
(python-send-region): Use python-proc, python-send-string.
(python-send-string): Send newlines too.  Callers changed.
(python-load-file): Re-written.
(python-eldoc-function): New.
(info-look): Don't require.
(python-after-info-look): New.  A modified version of former
top-level code for use with eval-after-load.
(python-maybe-jython, python-guess-indent): Use widened buffer.
(python-fill-paragraph): Re-written.
(python-mode): Fix outline-regexp.  Set outline-heading-end-regexp,
eldoc-print-current-symbol-info-function.  Add to eldoc-mode-hook.
This commit is contained in:
Stefan Monnier 2004-04-13 18:02:03 +00:00
parent 40899d7b75
commit 0578dead3a
2 changed files with 386 additions and 197 deletions

View file

@ -1,3 +1,28 @@
2004-04-13 Dave Love <fx@gnu.org>
* progmodes/python.el: Doc fixes. Changes for compiler warnings.
(syntax): Don't require.
(python) <defgroup>: Add :version.
(python-quote-syntax): Re-written.
(inferior-python-mode): Move stuff here from run-python and add
some more.
(python-preoutput-continuation, python-preoutput-result)
(python-dotty-syntax-table): New.
(python-describe-symbol): Use them.
(run-python): Move stuff to inferior-python-mode. Modify code
loaded into Python.
(python-send-region): Use python-proc, python-send-string.
(python-send-string): Send newlines too. Callers changed.
(python-load-file): Re-written.
(python-eldoc-function): New.
(info-look): Don't require.
(python-after-info-look): New. A modified version of former
top-level code for use with eval-after-load.
(python-maybe-jython, python-guess-indent): Use widened buffer.
(python-fill-paragraph): Re-written.
(python-mode): Fix outline-regexp. Set outline-heading-end-regexp,
eldoc-print-current-symbol-info-function. Add to eldoc-mode-hook.
2004-04-13 Stefan Monnier <monnier@iro.umontreal.ca>
* progmodes/python.el (run-python): Use compilation-shell-minor-mode.

View file

@ -38,7 +38,7 @@
;; This doesn't implement all the facilities of python-mode.el. Some
;; just need doing, e.g. catching exceptions in the inferior Python
;; buffer (but see M-x pdb for debugging). [Atually, the use of
;; buffer (but see M-x pdb for debugging). [Actually, the use of
;; `compilation-minor-mode' now is probably enough for that.] Others
;; don't seem appropriate. For instance, `forward-into-nomenclature'
;; should be done separately, since it's not specific to Python, and
@ -56,16 +56,25 @@
;; bindings are changed to obey Emacs conventions, and things like
;; marking blocks and `beginning-of-defun' behave differently.
;; TODO: See various Fixmes below. It should be possible to arrange
;; some sort of completion using the inferior interpreter.
;;; Code:
(require 'syntax) ; ensure appropriate version
;; It's messy to autoload the relevant comint functions so that comint
;; is only required when inferior Python is used.
(require 'comint)
(eval-when-compile
(require 'compile)
(autoload 'Info-last "info")
(autoload 'Info-exit "info")
(autoload 'info-lookup-maybe-add-help "info-look"))
(autoload 'compilation-start "compile") ; spurious compiler warning anyway
(defgroup python nil
"Silly walks in the Python language"
:group 'languages
:version "21.4"
:link '(emacs-commentary-link "python"))
;;;###autoload
@ -101,7 +110,8 @@
(group (syntax string-quote)) ; maybe gets property
(backref 2) ; per first quote
(group (backref 2)))) ; maybe gets property
(1 (python-quote-syntax 1)) (2 (python-quote-syntax 2))
(1 (python-quote-syntax 1))
(2 (python-quote-syntax 2))
(3 (python-quote-syntax 3)))
;; This doesn't really help.
;;; (,(rx (and ?\\ (group ?\n))) (1 " "))
@ -110,40 +120,43 @@
(defun python-quote-syntax (n)
"Put `syntax-table' property correctly on triple quote.
Used for syntactic keywords. N is the match number (1, 2 or 3)."
;; Given a triple quote, we have to look backwards for a previous
;; occurrence of the sequence to know whether this is an opening or
;; closing triple. We also have to sort out a possible prefix --
;; well, we don't _have_ to, but I think it should be treated as
;; part of the string.
(let ((tag (save-excursion
(goto-char (match-beginning 0))
(unless (eq ?\\ (char-before))
(cond
;; Look for a previous string fence.
((or (zerop (skip-syntax-backward "^|"))
(bobp))
'start) ; no previous string fence
;; Check fence on a matching quote.
((eq (char-before) (char-after (match-beginning 2)))
(if (eq (char-before) (char-after))
'end ; fence ended string
'start)) ; began string
;; Check for matching prefixed string.
((and (memq (char-before) '(?u ?U ?r ?R))
(skip-chars-forward "rR"))
(if (eq (char-after) (char-after (match-beginning 2)))
'end)) ; else non-matching: return nil
;; For non-matching quote, start new string.
((/= (char-before) (char-after))
'start))))))
(if (if (eq tag 'start) ; Maybe starts a new string.
;; Initial property if this is the prefix (empty or not) or
;; isn't the prefix and the prefix is empty.
(or (= n 1) (and (= n 2) (= (match-beginning 1) (match-end 1))))
(and (eq tag 'end) ; Maybe ends existing string.
(= n 3))) ; Match is at end.
(eval-when-compile (string-to-syntax "|"))
;; Otherwise the property is nil, which is OK.
;; Given a triple quote, we have to check the context to know
;; whether this is an opening or closing triple or whether it's
;; quoted anyhow, and should be ignored. (For that we need to do
;; the same job as `syntax-ppss' to be correct and it seems to be OK
;; to use it here despite initial worries.) We also have to sort
;; out a possible prefix -- well, we don't _have_ to, but I think it
;; should be treated as part of the string.
;; Test cases:
;; ur"""ar""" x='"' # """
;; x = ''' """ ' a
;; '''
;; x '"""' x
(save-excursion
(goto-char (match-beginning 0))
(unless (eq ?\\ (char-before))
(cond
;; Consider property for the last char if in a fenced string.
((= n 3)
(let ((syntax (syntax-ppss)))
(when (eq t (nth 3 syntax)) ; after unclosed fence
(goto-char (nth 8 syntax)) ; fence position
;; Skip any prefix.
(if (memq (char-after) '(?u ?U ?R ?r))
(skip-chars-forward "uUrR"))
;; Is it a matching sequence?
(if (eq (char-after) (char-after (match-beginning 2)))
(eval-when-compile (string-to-syntax "|"))))))
;; Consider property for initial char, accounting for prefixes.
((or (and (= n 2) ; not prefix
(= (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)))
(eval-when-compile (string-to-syntax "|")))))
;; Otherwise (we're in a non-matching string) the property is
;; nil, which is OK.
)))
;; This isn't currently in `font-lock-defaults' as probably not worth
@ -386,27 +399,29 @@ Continuation lines follow a backslash-terminated line starting a statement."
Set `python-indent' locally to the value guessed."
(interactive)
(save-excursion
(goto-char (point-min))
(let (done indent)
(while (and (not done) (not (eobp)))
(when (and (re-search-forward (rx (and ?: (0+ space)
(or (syntax comment-start)
line-end)))
nil 'move)
(python-open-block-statement-p))
(save-excursion
(python-beginning-of-statement)
(let ((initial (current-indentation)))
(if (zerop (python-next-statement))
(setq indent (- (current-indentation) initial)))
(if (and (>= indent 2) (<= indent 8)) ; sanity check
(setq done t))))))
(when done
(when (/= indent (default-value 'python-indent))
(set (make-local-variable 'python-indent) indent)
(unless (= tab-width python-indent)
(setq indent-tabs-mode nil)))
indent))))
(save-restriction
(widen)
(goto-char (point-min))
(let (done indent)
(while (and (not done) (not (eobp)))
(when (and (re-search-forward (rx (and ?: (0+ space)
(or (syntax comment-start)
line-end)))
nil 'move)
(python-open-block-statement-p))
(save-excursion
(python-beginning-of-statement)
(let ((initial (current-indentation)))
(if (zerop (python-next-statement))
(setq indent (- (current-indentation) initial)))
(if (and (>= indent 2) (<= indent 8)) ; sanity check
(setq done t))))))
(when done
(when (/= indent (default-value 'python-indent))
(set (make-local-variable 'python-indent) indent)
(unless (= tab-width python-indent)
(setq indent-tabs-mode nil)))
indent)))))
(defun python-calculate-indentation ()
"Calculate Python indentation for line at point."
@ -884,8 +899,6 @@ If not at the end of line's indentation, or on a comment line, just call
(defvar python-saved-check-command nil
"Internal use.")
(autoload 'compilation-start "compile")
;; After `sgml-validate-command'.
(defun python-check (command)
"Check a Python file (default current buffer's file).
@ -950,6 +963,13 @@ to be the new process's buffer. If you only run one process, this will
do the right thing. If you run multiple processes, you can change
`python-buffer' to another process buffer with \\[set-variable].")
(defconst python-compilation-regexp-alist
`((,(rx (and line-start (1+ (any " \t")) "File \""
(group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c
"\", line " (group (1+ digit))))
1 python-compilation-line-number))
"`compilation-error-regexp-alist' for inferior Python.")
(define-derived-mode inferior-python-mode comint-mode "Inferior Python"
"Major mode for interacting with an inferior Python process.
A Python process can be started with \\[run-python].
@ -973,7 +993,14 @@ For running multiple processes in multiple buffers, see `python-buffer'.
;; Fixme: Maybe install some python-mode bindings too.
(define-key inferior-python-mode-map "\C-c\C-l" 'python-load-file)
(define-key inferior-python-mode-map "\C-c\C-z" 'python-switch-to-python)
(setq comint-input-filter #'python-input-filter))
(add-hook 'comint-input-filter-functions 'python-input-filter nil t)
(add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter
nil t)
;; Still required by `comint-redirect-send-command', for instance:
(set (make-local-variable 'comint-prompt-regexp) "^\\([>.]\\{3\\} \\)+")
(set (make-local-variable 'compilation-error-regexp-alist)
python-compilation-regexp-alist)
(compilation-shell-minor-mode 1))
(defcustom inferior-python-filter-regexp "\\`\\s-*\\S-?\\S-?\\s-*\\'"
"*Input matching this regexp is not saved on the history list.
@ -989,7 +1016,7 @@ Default ignores all inputs of 0, 1, or 2 non-blank characters."
(defun python-input-filter (str)
"`comint-input-filter' function for inferior Python.
Don't save anything matching `inferior-python-filter-regexp'.
Don't save anything for STR matching `inferior-python-filter-regexp'.
Also resets variables for adjusting error messages."
(setq python-orig-file nil
python-orig-start-line 1)
@ -1005,17 +1032,6 @@ Also resets variables for adjusting error messages."
(t (let ((pos (string-match "[^ \t]" string)))
(if pos (python-args-to-list (substring string pos))))))))
(defvar compilation-minor-mode-map)
(defvar compilation-error-list)
(defvar compilation-parsing-end)
(defconst python-compilation-regexp-alist
`((,(rx (and line-start (1+ (any " \t")) "File \""
(group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c
"\", line " (group (1+ digit))))
1 python-compilation-line-number))
"`compilation-error-regexp-alist' for inferior Python.")
(defun python-compilation-line-number (file col)
"Return error descriptor of error found for FILE, column COL.
Used as line-number hook function in `python-compilation-regexp-alist'."
@ -1028,44 +1044,90 @@ Used as line-number hook function in `python-compilation-regexp-alist'."
(+ line (1- python-orig-start-line))
nil)))
(defvar python-preoutput-result nil
"Data from output line 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'.")
;; 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."
(cond ((and (string-match "\\`[.>]\\{3\\} \\'" s)
(/= (let ((inhibit-field-text-motion t))
(line-beginning-position))
(point)))
"")
((string= s "_emacs_ok\n")
(when python-preoutput-continuation
(funcall python-preoutput-continuation)
(setq python-preoutput-continuation nil))
"")
((string-match "_emacs_out \\(.*\\)\n" s)
(setq python-preoutput-result (match-string 1 s))
"")
(t s)))
;;;###autoload
(defun run-python (cmd)
(defun run-python (&optional cmd noshow)
"Run an inferior Python process, input and output via buffer *Python*.
CMD is the Python command to run.
CMD is the Python command to run. NOSHOW non-nil means don't show the
buffer automatically.
If there is a process already running in `*Python*', switch to
that buffer. With argument, allows you to edit the initial
command line (default is the value of `python-command'); `-i' and
`-c' args will be added to this to support evaluations in the
REPL. Runs the hooks `inferior-python-mode-hook' \(after the
`comint-mode-hook' is run). \(Type \\[describe-mode] in the
process buffer for a list of commands.)"
that buffer. Interactively a prefix arg, allows you to edit the initial
command line (default is the value of `python-command'); `-i' etc. args
will be added to this as appropriate. Runs the hooks
`inferior-python-mode-hook' (after the `comint-mode-hook' is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
(interactive (list (if current-prefix-arg
(read-string "Run Python: " python-command)
python-command)))
(unless cmd (setq cmd python-python-command))
(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.
(if (not (comint-check-proc "*Python*"))
(let ((cmdlist (append (python-args-to-list cmd)
'("-i" "-c" "\
from os import remove as _emacs_rem
def _emacs_execfile (file):
try:
execfile (file)
finally:
_emacs_rem (file)
"))))
(set-buffer (apply 'make-comint "Python" (car cmdlist) nil
(cdr cmdlist)))
(inferior-python-mode)
(setq comint-output-filter-functions nil)))
(setq python-command cmd)
(setq python-buffer "*Python*")
(pop-to-buffer "*Python*")
(set (make-local-variable 'compilation-error-regexp-alist)
python-compilation-regexp-alist)
(compilation-shell-minor-mode 1)
(add-hook 'comint-input-filter-functions 'python-input-filter nil t))
(unless (comint-check-proc "*Python*")
(let ((cmdlist (append (python-args-to-list cmd) '("-i"))))
(set-buffer (apply 'make-comint "Python" (car cmdlist) nil
(cdr cmdlist))))
(inferior-python-mode)
;; Load function defintions we need.
;; Before the preoutput function was used, this was done via -c in
;; cmdlist, but that loses the banner and doesn't run the startup
;; file.
(python-send-string "\
def _emacs_execfile (file): # execute file and remove it
from os import remove
try: execfile (file, globals (), globals ())
finally: remove (file)
def _emacs_args (name): # get arglist of name for eldoc &c
import inspect
parts = name.split ('.')
if len (parts) > 1:
try: exec 'import ' + parts[0]
except: return None
try: exec 'func='+name # lose if name is keyword or undefined
except: return None
if inspect.isbuiltin (func):
doc = func.__doc__
if doc.find (' ->') != -1:
print '_emacs_out', doc.split (' ->')[0]
elif doc.find ('\n') != -1:
print '_emacs_out', doc.split ('\n')[0]
return None
if inspect.ismethod (func): func = func.im_func
if not inspect.isfunction (func):
return None
(args, varargs, varkw, defaults) = inspect.getargspec (func)
print '_emacs_out', func.__name__+inspect.formatargspec (args, varargs, varkw, defaults)
print '_emacs_ok'"))
(unless noshow (pop-to-buffer (setq python-buffer "*Python*"))))
(defun python-mouse-2-command (event)
"Command bound to `mouse-2' in inferior Python buffer.
@ -1114,7 +1176,7 @@ Selects Comint or Compilation mode command as appropriate."
;; comint-filter the first two lines of the traceback?
(interactive "r")
(let* ((f (make-temp-file "py"))
(command (format "_emacs_execfile(%S)\n" f))
(command (format "_emacs_execfile(%S)" f))
(orig-file (buffer-file-name))
(orig-line (save-restriction
(widen)
@ -1126,22 +1188,21 @@ Selects Comint or Compilation mode command as appropriate."
(write-region start end f t 'nomsg)
(when python-buffer
(with-current-buffer python-buffer
(let ((end (marker-position (process-mark
(get-buffer-process python-buffer)))))
(let ((end (marker-position (process-mark (python-proc)))))
(set (make-local-variable 'python-orig-file) orig-file)
(set (make-local-variable 'python-orig-start-line) orig-line)
(set (make-local-variable 'compilation-error-list) nil)
;; (set (make-local-variable 'compilation-old-error-list) nil)
(let ((comint-input-filter-functions
(delete 'python-input-filter comint-input-filter-functions)))
(comint-send-string (python-proc) command))
(python-send-string command))
(set-marker compilation-parsing-end end)
(setq compilation-last-buffer (current-buffer)))))))
(defun python-send-string (string)
"Evaluate STRING in inferior Python process."
(interactive "sPython command: ")
(comint-send-string (python-proc) string))
(comint-send-string (python-proc) string)
(comint-send-string (python-proc) "\n\n"))
(defun python-send-buffer ()
"Send the current buffer to the inferior Python process."
@ -1198,19 +1259,31 @@ module-qualified names."
(comint-check-source file-name) ; Check to see if buffer needs saved.
(setq python-prev-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
(comint-send-string
(python-proc)
(if (string-match "\\.py\\'" file-name)
;; Fixme: make sure the directory is in the path list
(let ((module (file-name-sans-extension
(file-name-nondirectory file-name))))
(format "\
if globals().has_key('%s'):
reload(%s)
else:
import %s
(when python-buffer
(with-current-buffer python-buffer
(let ((end (marker-position (process-mark (python-proc)))))
(set (make-local-variable 'compilation-error-list) nil)
;; (set (make-local-variable 'compilation-old-error-list) nil)
(let ((comint-input-filter-functions
(delete 'python-input-filter comint-input-filter-functions)))
(python-send-string
(if (string-match "\\.py\\'" file-name)
;; Fixme: make sure the directory is in the path list
(let ((module (file-name-sans-extension
(file-name-nondirectory file-name))))
(set (make-local-variable 'python-orig-file) nil)
(set (make-local-variable 'python-orig-start-line) nil)
(format "\
try:
if globals().has_key(%S): reload(%s)
else: import %s
except: None
" module module module))
(format "execfile('%s')\n" filename))))
(set (make-local-variable 'python-orig-file) file-name)
(set (make-local-variable 'python-orig-start-line) 1)
(format "execfile('%s')" file-name))))
(set-marker compilation-parsing-end end)
(setq compilation-last-buffer (current-buffer))))))
;; Fixme: Should this start a process if there isn't one? (Unlike cmuscheme.)
(defun python-proc ()
@ -1222,74 +1295,114 @@ else:
;;;; Context-sensitive help.
;; Fixme: Investigate adapting this to eldoc.
(defconst python-dotty-syntax-table
(let ((table (make-syntax-table)))
(set-char-table-parent table python-mode-syntax-table)
(modify-syntax-entry ?. "_" table)
table)
"Syntax table giving `.' symbol syntax.
Otherwise inherits from `python-mode-syntax-table'.")
;; Fixme: Should this actually be used instead of info-look, i.e. be
;; bound to C-h S?
(defun python-describe-symbol (symbol)
"Get help on SYMBOL using `pydoc'.
Interactively, prompt for symbol."
;; Note that we do this in the inferior process, not a separate one to
;; ensure the environment is appropriate.
(interactive
(let ((symbol (current-word))
(let ((symbol (with-syntax-table python-dotty-syntax-table
(current-word)))
(enable-recursive-minibuffers t)
val)
(setq val (read-string (if symbol
(format "Describe variable (default %s): "
symbol)
"Describe variable: ")
"Describe symbol: ")
nil nil symbol))
(list (or val symbol))))
(if (equal symbol "") (error "No symbol"))
(let* ((mod (if (string-match "\\(\\.[^.]+\\)\\'" symbol)
(substring symbol 0 (match-beginning 1))))
(import (when mod (concat "import " mod "\n"))))
(with-output-to-temp-buffer "*Python Output*"
(princ (shell-command-to-string
(format "%s -c 'import pydoc %s
try: pydoc.help(%S)
except: print \"No help available on:\", %S'"
;; Fixme: Is this necessarily right?
(car (split-string python-command))
(or import "") symbol symbol))))))
(let* ((func `(lambda ()
(comint-redirect-send-command (format "help(%S)\n" ,symbol)
"*Help*" nil))))
;; Ensure we have a suitable help buffer.
(let (temp-buffer-show-hook) ; avoid xref stuff
(with-output-to-temp-buffer "*Help*"
(with-current-buffer standard-output
(set (make-local-variable 'comint-redirect-subvert-readonly) t))))
(if (and python-buffer (get-buffer python-buffer))
(with-current-buffer python-buffer
(funcall func))
(setq python-preoutput-continuation func)
(run-python nil t))))
(add-to-list 'debug-ignored-errors "^No symbol")
;; Fixme: try to make it work with point in the arglist. Also, is
;; there anything reasonable we can do with random methods?
;; (Currently only works with functions.)
(defun python-eldoc-function ()
"`eldoc-print-current-symbol-info' for Python.
Only works when point is in a function name, not its arglist, for instance.
Assumes an inferior Python is running."
(let ((symbol (with-syntax-table python-dotty-syntax-table
(current-word)))
(proc (and python-buffer (python-proc))))
(when (and proc symbol)
(python-send-string
(format "_emacs_args(%S)" symbol))
(setq python-preoutput-result nil)
(accept-process-output proc 1)
python-preoutput-result)))
;;;; Info-look functionality.
(require 'info-look)
;; We should be able to add info-lookup specs without loading the file first.
;; E.g. by setting a buffer-local var or something like that. --Stef
(let ((version (let ((s (shell-command-to-string (concat python-command
" -V"))))
(string-match "^Python \\([0-9]+\\.[0-9]+\\>\\)" s)
(match-string 1 s)))
;; Whether info files have a Python version suffix, e.g. in Debian.
(versioned (save-window-excursion
(condition-case ()
;; The call to `info' might create a new frame if
;; pop-up-frames or special-display-buffers are used.
;; So I disabled it until we find a better way
;; to handle this situation. Maybe Debian should just
;; fix their install somehow. --Stef
;; (progn (info "(python2.3-lib)Miscellaneous Index")
;; (Info-last)
;; (Info-exit)
;; t)
nil
(error nil)))))
(info-lookup-maybe-add-help
:mode 'python-mode
:regexp "[[:alnum:]_]+"
:doc-spec
;; Fixme: Add python-ref? Can this reasonably be made specific
;; to indices with different rules?
(if versioned
'((,(concat "(python" version "-lib)Module Index"))
(,(concat "(python" version "-lib)Class-Exception-Object Index"))
(,(concat "(python" version "-lib)Function-Method-Variable Index"))
(,(concat "(python" version "-lib)Miscellaneous Index")))
'(("(python-lib)Module Index")
("(python-lib)Class-Exception-Object Index")
("(python-lib)Function-Method-Variable Index")
("(python-lib)Miscellaneous Index")))))
(defun python-after-info-look ()
"Set up info-look for Python.
Used with `eval-after-load'."
(let* ((version (let ((s (shell-command-to-string (concat python-command
" -V"))))
(string-match "^Python \\([0-9]+\\.[0-9]+\\>\\)" s)
(match-string 1 s)))
;; Whether info files have a Python version suffix, e.g. in Debian.
(versioned
(with-temp-buffer
(Info-mode)
(condition-case ()
(Info-goto-node (format "(python%s-lib)Miscellaneous Index"
version))
(error nil)))))
(info-lookup-maybe-add-help
:mode 'python-mode
:regexp "[[:alnum:]_]+"
:doc-spec
;; Fixme: Can this reasonably be made specific to indices with
;; different rules? Is the order of indices optimal?
;; (Miscellaneous in -ref first prefers lookup of keywords, for
;; instance.)
(if versioned
;; The empty prefix just gets us highlighted terms.
`((,(concat "(python" version "-ref)Miscellaneous Index") nil "")
(,(concat "(python" version "-ref)Module Index" nil ""))
(,(concat "(python" version "-ref)Function-Method-Variable Index"
nil ""))
(,(concat "(python" version "-ref)Class-Exception-Object Index"
nil ""))
(,(concat "(python" version "-lib)Module Index" nil ""))
(,(concat "(python" version "-lib)Class-Exception-Object Index"
nil ""))
(,(concat "(python" version "-lib)Function-Method-Variable Index"
nil ""))
(,(concat "(python" version "-lib)Miscellaneous Index" nil "")))
'(("(python-ref)Miscellaneous Index" nil "")
("(python-ref)Module Index" nil "")
("(python-ref)Function-Method-Variable Index" nil "")
("(python-ref)Class-Exception-Object Index" nil "")
("(python-lib)Module Index" nil "")
("(python-lib)Class-Exception-Object Index" nil "")
("(python-lib)Function-Method-Variable Index" nil "")
("(python-lib)Miscellaneous Index" nil ""))))))
(eval-after-load "info-look" '(python-after-info-look))
;;;; Miscellancy.
@ -1309,34 +1422,65 @@ The criterion is either a match for `jython-mode' via
`python-jython-packages'."
;; The logic is taken from python-mode.el.
(save-excursion
(goto-char (point-min))
(let ((interpreter (if (looking-at auto-mode-interpreter-regexp)
(match-string 2))))
(if (and interpreter (eq 'jython-mode
(cdr (assoc (file-name-nondirectory interpreter)
interpreter-mode-alist))))
(jython-mode)
(if (catch 'done
(while (re-search-forward
(rx (and line-start (or "import" "from") (1+ space)
(group (1+ (not (any " \t\n."))))))
(+ (point) 10000) ; Probably not worth customizing.
t)
(if (member (match-string 1) python-jython-packages)
(throw 'done t))))
(jython-mode))))))
(save-restriction
(widen)
(goto-char (point-min))
(let ((interpreter (if (looking-at auto-mode-interpreter-regexp)
(match-string 2))))
(if (and interpreter (eq 'jython-mode
(cdr (assoc (file-name-nondirectory
interpreter)
interpreter-mode-alist))))
(jython-mode)
(if (catch 'done
(while (re-search-forward
(rx (and line-start (or "import" "from") (1+ space)
(group (1+ (not (any " \t\n."))))))
10000 ; Probably not worth customizing.
t)
(if (member (match-string 1) python-jython-packages)
(throw 'done t))))
(jython-mode)))))))
(defun python-fill-paragraph (&optional justify)
"Like \\[fill-paragraph], but handle comments and multi-line strings.
"`fill-paragraph-function' handling comments and multi-line strings.
If any of the current line is a comment, fill the comment or the
paragraph of it that point is in, preserving the comment's indentation
and initial comment characters."
paragraph of it that point is in, preserving the comment's
indentation and initial comment characters. Similarly if the end
of the current line is in or at the end of a multi-line string.
Otherwise, do nothing."
(interactive "P")
(or (fill-comment-paragraph justify)
(let ((paragraph-start (concat paragraph-start
"\\|\\s-*\\(:?#\\s\"\\|\\s|\\|#")))
(fill-paragraph justify))
t))
;; The `paragraph-start' and `paragraph-separate' variables
;; don't allow us to delimit the last paragraph in a multi-line
;; string properly, so narrow to the string and then fill around
;; (the end of) the current line.
(save-excursion
(end-of-line)
(let* ((syntax (syntax-ppss))
(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)))))
((re-search-backward "\\s|\\s-*\\=" nil t) ; end of fenced
; string
(forward-char)
(setq end (point))
(condition-case ()
(progn (backward-sexp)
(setq start (point)))
(error nil))))
(when end
(save-restriction
(narrow-to-region start end)
(goto-char orig)
(fill-paragraph justify))))))
t)
(defun python-shift-left (start end &optional count)
"Shift lines in region COUNT (the prefix arg) columns to the left.
@ -1377,7 +1521,8 @@ END lie."
(defun python-outline-level ()
"`outline-level' function for Python mode.
The level is the number of `python-indent' steps of indentation."
The level is the number of `python-indent' steps of indentation
of current line."
(/ (current-indentation) python-indent))
;; Fixme: Consider top-level assignments, imports, &c.
@ -1411,17 +1556,23 @@ Uses `python-beginning-of-block', `python-end-of-block'."
;;;; Modes.
(defvar outline-heading-end-regexp)
(defvar eldoc-print-current-symbol-info-function)
;;;###autoload
(define-derived-mode python-mode fundamental-mode "Python"
"Major mode for editing Python files.
Turns on Font Lock mode unconditionally since it is required for correct
parsing of the source.
See also `jython-mode', which is actually invoked if the buffer appears to
contain Jython code.
contain Jython code. See also `run-python' and associated Python mode
commands for running Python under Emacs.
The Emacs commands which work with `defun's, e.g. \\[beginning-of-defun], deal
with nested `def' and `class' blocks. They take the innermost one as
current without distinguishing method and class definitions.
current without distinguishing method and class definitions. Used multiple
times, they move over others at the same indentation level until they reach
the end of definitions at that level, when they move up a level.
\\<python-mode-map>
Colon is electric: it outdents the line if appropriate, e.g. for
an else statement. \\[python-backspace] at the beginning of an indented statement
@ -1430,6 +1581,13 @@ deletes a charcter backward. TAB indents the current line relative to
the preceding code. Successive TABs, with no intervening command, cycle
through the possibilities for indentation on the basis of enclosing blocks.
\\[fill-paragraph] fills comments and multiline strings appropriately, but has no
effect outside them.
Supports Eldoc mode (only for functions, using a Python process),
Info-Look and Imenu. In Outline minor mode, `class' and `def'
lines count as headers.
\\{python-mode-map}"
:group 'python
(set (make-local-variable 'font-lock-defaults)
@ -1451,7 +1609,8 @@ through the possibilities for indentation on the basis of enclosing blocks.
(set (make-local-variable 'add-log-current-defun-function)
#'python-current-defun)
;; Fixme: Generalize to do all blocks?
(set (make-local-variable 'outline-regexp) "\\s-+\\(def\\|class\\)\\>")
(set (make-local-variable 'outline-regexp) "\\s-*\\(def\\|class\\)\\>")
(set (make-local-variable 'outline-heading-end-regexp) ":\\s-*\n")
(set (make-local-variable 'outline-level) #'python-outline-level)
(set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
(make-local-variable 'python-saved-check-command)
@ -1459,6 +1618,10 @@ through the possibilities for indentation on the basis of enclosing blocks.
'python-beginning-of-defun)
(set (make-local-variable 'end-of-defun-function) 'python-end-of-defun)
(setq imenu-create-index-function #'python-imenu-create-index)
(set (make-local-variable 'eldoc-print-current-symbol-info-function)
#'python-eldoc-function)
(add-hook 'eldoc-mode-hook
'(lambda () (run-python 0 t)) nil t) ; need it running
(unless font-lock-mode (font-lock-mode 1))
(when python-guess-indent (python-guess-indent))
(set (make-local-variable 'python-command) python-python-command)
@ -1471,6 +1634,7 @@ through the possibilities for indentation on the basis of enclosing blocks.
'(lambda ()
"Turn on Indent Tabs mode."
(set (make-local-variable 'indent-tabs-mode) t)))
(custom-add-option 'python-mode-hook 'turn-on-eldoc-mode)
;;;###autoload
(define-derived-mode jython-mode python-mode "Jython"