ipython.el
514 lines
| 21.3 KiB
| text/x-common-lisp
|
EmacsLispLexer
fperez
|
r0 | ;;; ipython.el --- Adds support for IPython to python-mode.el | ||
;; Copyright (C) 2002, 2003, 2004, 2005 Alexander Schmolck | ||||
;; Author: Alexander Schmolck | ||||
;; Keywords: ipython python languages oop | ||||
Thomas Kluyver
|
r4086 | ;; URL: http://ipython.org | ||
fperez
|
r0 | ;; Compatibility: Emacs21, XEmacs21 | ||
;; FIXME: #$@! INPUT RING | ||||
Fernando Perez
|
r2553 | (defconst ipython-version "0.11" | ||
"Tied to IPython main version number.") | ||||
fperez
|
r0 | |||
Fernando Perez
|
r1329 | ;;; Commentary | ||
fperez
|
r0 | ;; This library makes all the functionality python-mode has when running with | ||
;; the normal python-interpreter available for ipython, too. It also enables a | ||||
walter.doerwald
|
r359 | ;; persistent py-shell command history across sessions (if you exit python | ||
fperez
|
r0 | ;; with C-d in py-shell) and defines the command `ipython-to-doctest', which | ||
;; can be used to convert bits of a ipython session into something that can be | ||||
;; used for doctests. To install, put this file somewhere in your emacs | ||||
;; `load-path' [1] and add the following line to your ~/.emacs file (the first | ||||
Pete Aykroyd
|
r5371 | ;; line only needed if the default (``"ipython"``) is wrong or ipython is not | ||
;; in your `exec-path'):: | ||||
fperez
|
r0 | ;; | ||
;; (setq ipython-command "/SOME-PATH/ipython") | ||||
;; (require 'ipython) | ||||
;; | ||||
;; Ipython will be set as the default python shell, but only if the ipython | ||||
;; executable is in the path. For ipython sessions autocompletion with <tab> | ||||
;; is also enabled (experimental feature!). Please also note that all the | ||||
;; terminal functions in py-shell are handled by emacs's comint, **not** by | ||||
;; (i)python, so importing readline etc. will have 0 effect. | ||||
;; | ||||
;; To start an interactive ipython session run `py-shell' with ``M-x py-shell`` | ||||
;; (or the default keybinding ``C-c C-!``). | ||||
;; | ||||
Fernando Perez
|
r1328 | ;; You can customize the arguments passed to the IPython instance at startup by | ||
;; setting the ``py-python-command-args`` variable. For example, to start | ||||
Fernando Perez
|
r1329 | ;; always in ``pylab`` mode with hardcoded light-background colors, you can | ||
Fernando Perez
|
r5425 | ;; use the following, *after* the ``(require 'ipython)`` line:: | ||
Fernando Perez
|
r1329 | ;; | ||
Pete Aykroyd
|
r5371 | ;; (setq-default py-python-command-args '("--pylab" "--colors=LightBG")) | ||
Fernando Perez
|
r1328 | ;; | ||
;; | ||||
fperez
|
r0 | ;; NOTE: This mode is currently somewhat alpha and although I hope that it | ||
;; will work fine for most cases, doing certain things (like the | ||||
;; autocompletion and a decent scheme to switch between python interpreters) | ||||
;; properly will also require changes to ipython that will likely have to wait | ||||
;; for a larger rewrite scheduled some time in the future. | ||||
Fernando Perez
|
r1329 | ;; | ||
fperez
|
r0 | ;; | ||
;; Further note that I don't know whether this runs under windows or not and | ||||
;; that if it doesn't I can't really help much, not being afflicted myself. | ||||
;; | ||||
;; | ||||
;; Hints for effective usage | ||||
;; ------------------------- | ||||
;; | ||||
Fernando Perez
|
r1328 | ;; - IMO the best feature by far of the ipython/emacs combo is how much easier | ||
;; it makes it to find and fix bugs thanks to the ``%pdb on or %debug``/ | ||||
Fernando Perez
|
r5423 | ;; pdbtrack combo. **NOTE** for this feature to work, you must turn coloring | ||
;; off, at least during your debug session. Type ``%colors nocolor`` before | ||||
;; debugging and file tracking will work, you can re-enable it with ``%colors | ||||
;; linux`` or ``%colors lightbg`` (depending on your preference) when | ||||
;; finished debugging so you can have coloring for the rest of the session. | ||||
;; | ||||
;; Try it: first in the ipython to shell do ``%pdb on`` then do something | ||||
;; that will raise an exception (FIXME nice example), or type ``%debug`` | ||||
;; after the exception has been raised. You'll be amazed at how easy it is | ||||
;; to inspect the live objects in each stack frames and to jump to the | ||||
;; corresponding sourcecode locations as you walk up and down the stack trace | ||||
;; (even without ``%pdb on`` you can always use ``C-c -`` (`py-up-exception') | ||||
;; to jump to the corresponding source code locations). | ||||
fperez
|
r0 | ;; | ||
;; - emacs gives you much more powerful commandline editing and output searching | ||||
;; capabilities than ipython-standalone -- isearch is your friend if you | ||||
;; quickly want to print 'DEBUG ...' to stdout out etc. | ||||
;; | ||||
;; - This is not really specific to ipython, but for more convenient history | ||||
;; access you might want to add something like the following to *the beggining* | ||||
;; of your ``.emacs`` (if you want behavior that's more similar to stand-alone | ||||
;; ipython, you can change ``meta p`` etc. for ``control p``):: | ||||
;; | ||||
;; (require 'comint) | ||||
Fernando Perez
|
r1329 | ;; (define-key comint-mode-map [(meta p)] | ||
fperez
|
r0 | ;; 'comint-previous-matching-input-from-input) | ||
Fernando Perez
|
r1329 | ;; (define-key comint-mode-map [(meta n)] | ||
fperez
|
r0 | ;; 'comint-next-matching-input-from-input) | ||
;; (define-key comint-mode-map [(control meta n)] | ||||
;; 'comint-next-input) | ||||
;; (define-key comint-mode-map [(control meta p)] | ||||
;; 'comint-previous-input) | ||||
;; | ||||
;; - Be aware that if you customize py-python-command previously, this value | ||||
;; will override what ipython.el does (because loading the customization | ||||
;; variables comes later). | ||||
;; | ||||
;; Please send comments and feedback to the ipython-list | ||||
Fernando Perez
|
r1328 | ;; (<ipython-user@scipy.org>) where I (a.s.) or someone else will try to | ||
Fernando Perez
|
r1329 | ;; answer them (it helps if you specify your emacs version, OS etc; | ||
fperez
|
r0 | ;; familiarity with <http://www.catb.org/~esr/faqs/smart-questions.html> might | ||
;; speed up things further). | ||||
;; | ||||
;; Footnotes: | ||||
;; | ||||
;; [1] If you don't know what `load-path' is, C-h v load-path will tell | ||||
;; you; if required you can also add a new directory. So assuming that | ||||
;; ipython.el resides in ~/el/, put this in your emacs: | ||||
;; | ||||
;; | ||||
;; (add-to-list 'load-path "~/el") | ||||
;; (setq ipython-command "/some-path/ipython") | ||||
;; (require 'ipython) | ||||
;; | ||||
;; | ||||
;; | ||||
;; | ||||
;; TODO: | ||||
;; - do autocompletion properly | ||||
;; - implement a proper switching between python interpreters | ||||
;; | ||||
;; BUGS: | ||||
;; - neither:: | ||||
;; | ||||
;; (py-shell "-c print 'FOOBAR'") | ||||
Fernando Perez
|
r1329 | ;; | ||
fperez
|
r0 | ;; nor:: | ||
Fernando Perez
|
r1329 | ;; | ||
;; (let ((py-python-command-args (append py-python-command-args | ||||
fperez
|
r0 | ;; '("-c" "print 'FOOBAR'")))) | ||
;; (py-shell)) | ||||
;; | ||||
;; seem to print anything as they should | ||||
;; | ||||
;; - look into init priority issues with `py-python-command' (if it's set | ||||
;; via custom) | ||||
;;; Code | ||||
(require 'cl) | ||||
(require 'shell) | ||||
(require 'executable) | ||||
(require 'ansi-color) | ||||
Pete Aykroyd
|
r5371 | ;; XXX load python-mode, so that we can screw around with its variables | ||
;; this has the disadvantage that python-mode is loaded even if no | ||||
;; python-file is ever edited etc. but it means that `py-shell' works | ||||
;; without loading a python-file first. Obviously screwing around with | ||||
;; python-mode's variables like this is a mess, but well. | ||||
(require 'python-mode) | ||||
fperez
|
r0 | |||
(defcustom ipython-command "ipython" | ||||
"*Shell command used to start ipython." | ||||
Fernando Perez
|
r1329 | :type 'string | ||
fperez
|
r0 | :group 'python) | ||
;; Users can set this to nil | ||||
(defvar py-shell-initial-switch-buffers t | ||||
"If nil, don't switch to the *Python* buffer on the first call to | ||||
`py-shell'.") | ||||
(defvar ipython-backup-of-py-python-command nil | ||||
"HACK") | ||||
Fernando Perez
|
r1329 | |||
fperez
|
r0 | (defvar ipython-de-input-prompt-regexp "\\(?: | ||
In \\[[0-9]+\\]: *.* | ||||
----+> \\(.* | ||||
\\)[\n]?\\)\\|\\(?: | ||||
In \\[[0-9]+\\]: *\\(.* | ||||
\\)\\)\\|^[ ]\\{3\\}[.]\\{3,\\}: *\\(.* | ||||
\\)" | ||||
"A regular expression to match the IPython input prompt and the python | ||||
command after it. The first match group is for a command that is rewritten, | ||||
the second for a 'normal' command, and the third for a multiline command.") | ||||
(defvar ipython-de-output-prompt-regexp "^Out\\[[0-9]+\\]: " | ||||
"A regular expression to match the output prompt of IPython.") | ||||
(if (not (executable-find ipython-command)) | ||||
(message (format "Can't find executable %s - ipython.el *NOT* activated!!!" | ||||
ipython-command)) | ||||
Pete Aykroyd
|
r5371 | ;; change the default value of py-shell-name to ipython | ||
(setq-default py-shell-name ipython-command) | ||||
fperez
|
r0 | ;; turn on ansi colors for ipython and activate completion | ||
(defun ipython-shell-hook () | ||||
;; the following is to synchronize dir-changes | ||||
(make-local-variable 'shell-dirstack) | ||||
(setq shell-dirstack nil) | ||||
(make-local-variable 'shell-last-dir) | ||||
(setq shell-last-dir nil) | ||||
(make-local-variable 'shell-dirtrackp) | ||||
(setq shell-dirtrackp t) | ||||
(add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t) | ||||
(ansi-color-for-comint-mode-on) | ||||
(define-key py-shell-map [tab] 'ipython-complete) | ||||
jdh2358
|
r596 | ;; Add this so that tab-completion works both in X11 frames and inside | ||
;; terminals (such as when emacs is called with -nw). | ||||
(define-key py-shell-map "\t" 'ipython-complete) | ||||
fperez
|
r0 | ;;XXX this is really just a cheap hack, it only completes symbols in the | ||
;;interactive session -- useful nonetheless. | ||||
fperez
|
r294 | (define-key py-mode-map [(meta tab)] 'ipython-complete) | ||
Fernando Perez
|
r1329 | |||
fperez
|
r294 | ) | ||
fperez
|
r0 | (add-hook 'py-shell-hook 'ipython-shell-hook) | ||
;; Regular expression that describes tracebacks for IPython in context and | ||||
Fernando Perez
|
r1329 | ;; verbose mode. | ||
fperez
|
r0 | ;;Adapt python-mode settings for ipython. | ||
;; (this works for %xmode 'verbose' or 'context') | ||||
Fernando Perez
|
r1329 | ;; XXX putative regexps for syntax errors; unfortunately the | ||
fperez
|
r0 | ;; current python-mode traceback-line-re scheme is too primitive, | ||
;; so it's either matching syntax errors, *or* everything else | ||||
;; (XXX: should ask Fernando for a change) | ||||
;;"^ File \"\\(.*?\\)\", line \\([0-9]+\\).*\n.*\n.*\nSyntaxError:" | ||||
;;^ File \"\\(.*?\\)\", line \\([0-9]+\\)" | ||||
fperez
|
r294 | |||
fperez
|
r0 | (setq py-traceback-line-re | ||
fperez
|
r552 | "\\(^[^\t >].+?\\.py\\).*\n +[0-9]+[^\00]*?\n-+> \\([0-9]+\\)+") | ||
Fernando Perez
|
r1329 | |||
vivainio
|
r378 | ;; Recognize the ipython pdb, whose prompt is 'ipdb>' or 'ipydb>' | ||
;;instead of '(Pdb)' | ||||
(setq py-pdbtrack-input-prompt "\n[(<]*[Ii]?[Pp]y?db[>)]+ ") | ||||
fperez
|
r552 | (setq pydb-pydbtrack-input-prompt "\n[(]*ipydb[>)]+ ") | ||
Fernando Perez
|
r1329 | |||
fperez
|
r0 | (setq py-shell-input-prompt-1-regexp "^In \\[[0-9]+\\]: *" | ||
py-shell-input-prompt-2-regexp "^ [.][.][.]+: *" ) | ||||
;; select a suitable color-scheme | ||||
Kefu Chai
|
r4727 | (unless (delq nil | ||
Pete Aykroyd
|
r5372 | (mapcar (lambda (x) (eq (string-match "^--colors*" x) 0)) | ||
Kefu Chai
|
r4727 | py-python-command-args)) | ||
Pete Aykroyd
|
r5371 | (setq-default py-python-command-args | ||
(cons (format "--colors=%s" | ||||
(cond | ||||
((eq frame-background-mode 'dark) | ||||
"Linux") | ||||
((eq frame-background-mode 'light) | ||||
"LightBG") | ||||
(t ; default (backg-mode isn't always set by XEmacs) | ||||
"LightBG"))) | ||||
py-python-command-args))) | ||||
Pete Aykroyd
|
r5330 | (when (boundp 'py-python-command) | ||
(unless (equal ipython-backup-of-py-python-command py-python-command) | ||||
(setq ipython-backup-of-py-python-command py-python-command))) | ||||
(setq py-python-command ipython-command) | ||||
(when (boundp 'py-shell-name) | ||||
(setq py-shell-name ipython-command))) | ||||
fperez
|
r0 | |||
;; MODIFY py-shell so that it loads the editing history | ||||
(defadvice py-shell (around py-shell-with-history) | ||||
"Add persistent command-history support (in | ||||
$PYTHONHISTORY (or \"~/.ipython/history\", if we use IPython)). Also, if | ||||
`py-shell-initial-switch-buffers' is nil, it only switches to *Python* if that | ||||
buffer already exists." | ||||
(if (comint-check-proc "*Python*") | ||||
ad-do-it | ||||
(setq comint-input-ring-file-name | ||||
(if (string-equal py-python-command ipython-command) | ||||
(concat (or (getenv "IPYTHONDIR") "~/.ipython") "/history") | ||||
(or (getenv "PYTHONHISTORY") "~/.python-history.py"))) | ||||
(comint-read-input-ring t) | ||||
(let ((buf (current-buffer))) | ||||
ad-do-it | ||||
(unless py-shell-initial-switch-buffers | ||||
(switch-to-buffer-other-window buf))))) | ||||
(ad-activate 'py-shell) | ||||
;; (defadvice py-execute-region (before py-execute-buffer-ensure-process) | ||||
;; "HACK: test that ipython is already running before executing something. | ||||
;; Doing this properly seems not worth the bother (unless people actually | ||||
;; request it)." | ||||
;; (unless (comint-check-proc "*Python*") | ||||
;; (error "Sorry you have to first do M-x py-shell to send something to ipython."))) | ||||
;; (ad-activate 'py-execute-region) | ||||
(defadvice py-execute-region (around py-execute-buffer-ensure-process) | ||||
"HACK: if `py-shell' is not active or ASYNC is explicitly desired, fall back | ||||
Fernando Perez
|
r1329 | to python instead of ipython." | ||
fptest
|
r391 | (let ((py-which-shell (if (and (comint-check-proc "*Python*") (not async)) | ||
py-python-command | ||||
ipython-backup-of-py-python-command))) | ||||
fperez
|
r0 | ad-do-it)) | ||
(ad-activate 'py-execute-region) | ||||
(defun ipython-to-doctest (start end) | ||||
"Transform a cut-and-pasted bit from an IPython session into something that | ||||
looks like it came from a normal interactive python session, so that it can | ||||
be used in doctests. Example: | ||||
In [1]: import sys | ||||
Fernando Perez
|
r1329 | |||
fperez
|
r0 | In [2]: sys.stdout.write 'Hi!\n' | ||
------> sys.stdout.write ('Hi!\n') | ||||
Hi! | ||||
Fernando Perez
|
r1329 | |||
fperez
|
r0 | In [3]: 3 + 4 | ||
Out[3]: 7 | ||||
Fernando Perez
|
r1329 | |||
fperez
|
r0 | gets converted to: | ||
>>> import sys | ||||
>>> sys.stdout.write ('Hi!\n') | ||||
Hi! | ||||
>>> 3 + 4 | ||||
7 | ||||
" | ||||
(interactive "*r\n") | ||||
;(message (format "###DEBUG s:%de:%d" start end)) | ||||
(save-excursion | ||||
(save-match-data | ||||
Fernando Perez
|
r1329 | ;; replace ``In [3]: bla`` with ``>>> bla`` and | ||
fperez
|
r0 | ;; ``... : bla`` with ``... bla`` | ||
(goto-char start) | ||||
(while (re-search-forward ipython-de-input-prompt-regexp end t) | ||||
;(message "finding 1") | ||||
(cond ((match-string 3) ;continued | ||||
(replace-match "... \\3" t nil)) | ||||
(t | ||||
(replace-match ">>> \\1\\2" t nil)))) | ||||
;; replace `` | ||||
(goto-char start) | ||||
(while (re-search-forward ipython-de-output-prompt-regexp end t) | ||||
(replace-match "" t nil))))) | ||||
Fernando Perez
|
r1329 | (defvar ipython-completion-command-string | ||
Pete Aykroyd
|
r5341 | "print(';'.join(get_ipython().complete('%s', '%s')[1])) #PYTHON-MODE SILENT\n" | ||
fperez
|
r0 | "The string send to ipython to query for all possible completions") | ||
;; xemacs doesn't have `comint-preoutput-filter-functions' so we'll try the | ||||
;; following wonderful hack to work around this case | ||||
(if (featurep 'xemacs) | ||||
;;xemacs | ||||
(defun ipython-complete () | ||||
"Try to complete the python symbol before point. Only knows about the stuff | ||||
in the current *Python* session." | ||||
(interactive) | ||||
(let* ((ugly-return nil) | ||||
(sep ";") | ||||
fperez
|
r224 | (python-process (or (get-buffer-process (current-buffer)) | ||
;XXX hack for .py buffers | ||||
(get-process py-which-bufname))) | ||||
fperez
|
r0 | ;; XXX currently we go backwards to find the beginning of an | ||
;; expression part; a more powerful approach in the future might be | ||||
;; to let ipython have the complete line, so that context can be used | ||||
;; to do things like filename completion etc. | ||||
(beg (save-excursion (skip-chars-backward "a-z0-9A-Z_." (point-at-bol)) | ||||
(point))) | ||||
(end (point)) | ||||
(pattern (buffer-substring-no-properties beg end)) | ||||
(completions nil) | ||||
(completion-table nil) | ||||
completion | ||||
(comint-output-filter-functions | ||||
Fernando Perez
|
r1329 | (append comint-output-filter-functions | ||
fperez
|
r0 | '(ansi-color-filter-apply | ||
Fernando Perez
|
r1329 | (lambda (string) | ||
fperez
|
r0 | ;(message (format "DEBUG filtering: %s" string)) | ||
(setq ugly-return (concat ugly-return string)) | ||||
Fernando Perez
|
r1329 | (delete-region comint-last-output-start | ||
fperez
|
r0 | (process-mark (get-buffer-process (current-buffer))))))))) | ||
Fernando Perez
|
r2553 | ;(message (format "#DEBUG pattern: '%s'" pattern)) | ||
Fernando Perez
|
r1329 | (process-send-string python-process | ||
fperez
|
r0 | (format ipython-completion-command-string pattern)) | ||
fperez
|
r224 | (accept-process-output python-process) | ||
Fernando Perez
|
r2553 | |||
;(message (format "DEBUG return: %s" ugly-return)) | ||||
Fernando Perez
|
r1329 | (setq completions | ||
fperez
|
r0 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) | ||
(setq completion-table (loop for str in completions | ||||
collect (list str nil))) | ||||
(setq completion (try-completion pattern completion-table)) | ||||
(cond ((eq completion t)) | ||||
((null completion) | ||||
(message "Can't find completion for \"%s\"" pattern) | ||||
(ding)) | ||||
((not (string= pattern completion)) | ||||
(delete-region beg end) | ||||
(insert completion)) | ||||
(t | ||||
(message "Making completion list...") | ||||
(with-output-to-temp-buffer "*Python Completions*" | ||||
(display-completion-list (all-completions pattern completion-table))) | ||||
(message "Making completion list...%s" "done"))))) | ||||
;; emacs | ||||
(defun ipython-complete () | ||||
"Try to complete the python symbol before point. Only knows about the stuff | ||||
in the current *Python* session." | ||||
(interactive) | ||||
(let* ((ugly-return nil) | ||||
(sep ";") | ||||
fperez
|
r224 | (python-process (or (get-buffer-process (current-buffer)) | ||
;XXX hack for .py buffers | ||||
(get-process py-which-bufname))) | ||||
fperez
|
r0 | ;; XXX currently we go backwards to find the beginning of an | ||
;; expression part; a more powerful approach in the future might be | ||||
;; to let ipython have the complete line, so that context can be used | ||||
;; to do things like filename completion etc. | ||||
Fernando Perez
|
r5422 | (beg (save-excursion (skip-chars-backward "a-z0-9A-Z_./\-" (point-at-bol)) | ||
Fernando Perez
|
r1329 | (point))) | ||
fperez
|
r0 | (end (point)) | ||
Pete Aykroyd
|
r5341 | (line (buffer-substring-no-properties (point-at-bol) end)) | ||
fperez
|
r0 | (pattern (buffer-substring-no-properties beg end)) | ||
(completions nil) | ||||
(completion-table nil) | ||||
completion | ||||
(comint-preoutput-filter-functions | ||||
Fernando Perez
|
r1329 | (append comint-preoutput-filter-functions | ||
fperez
|
r0 | '(ansi-color-filter-apply | ||
Fernando Perez
|
r1329 | (lambda (string) | ||
fperez
|
r0 | (setq ugly-return (concat ugly-return string)) | ||
""))))) | ||||
Fernando Perez
|
r1329 | (process-send-string python-process | ||
Pete Aykroyd
|
r5341 | (format ipython-completion-command-string pattern line)) | ||
fperez
|
r224 | (accept-process-output python-process) | ||
Fernando Perez
|
r1329 | (setq completions | ||
fperez
|
r0 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) | ||
;(message (format "DEBUG completions: %S" completions)) | ||||
(setq completion-table (loop for str in completions | ||||
collect (list str nil))) | ||||
(setq completion (try-completion pattern completion-table)) | ||||
(cond ((eq completion t)) | ||||
((null completion) | ||||
Pete Aykroyd
|
r5341 | (message "Can't find completion for \"%s\" based on line %s" pattern line) | ||
fperez
|
r0 | (ding)) | ||
((not (string= pattern completion)) | ||||
Pete Aykroyd
|
r5341 | (delete-region (- end (length pattern)) end) | ||
fperez
|
r0 | (insert completion)) | ||
(t | ||||
(message "Making completion list...") | ||||
(with-output-to-temp-buffer "*IPython Completions*" | ||||
(display-completion-list (all-completions pattern completion-table))) | ||||
(message "Making completion list...%s" "done"))))) | ||||
) | ||||
Pete Aykroyd
|
r5340 | ;;; if python-mode's keybinding for the tab key wins then py-shell-complete is called | ||
;;; instead of ipython-complete which result in hanging emacs since there is no shell | ||||
;;; process for python-mode to communicate with | ||||
(defadvice py-shell-complete | ||||
(around avoid-py-shell-complete activate) | ||||
(ipython-complete)) | ||||
fperez
|
r295 | ;;; autoindent support: patch sent in by Jin Liu <m.liu.jin@gmail.com>, | ||
;;; originally written by doxgen@newsmth.net | ||||
;;; Minor modifications by fperez for xemacs compatibility. | ||||
(defvar ipython-autoindent t | ||||
"If non-nil, enable autoindent for IPython shell through python-mode.") | ||||
(defvar ipython-indenting-buffer-name "*IPython Indentation Calculation*" | ||||
"Temporary buffer for indenting multiline statement.") | ||||
(defun ipython-get-indenting-buffer () | ||||
"Return a temporary buffer set in python-mode. Create one if necessary." | ||||
(let ((buf (get-buffer-create ipython-indenting-buffer-name))) | ||||
(set-buffer buf) | ||||
(unless (eq major-mode 'python-mode) | ||||
(python-mode)) | ||||
buf)) | ||||
(defvar ipython-indentation-string nil | ||||
"Indentation for the next line in a multiline statement.") | ||||
(defun ipython-send-and-indent () | ||||
"Send the current line to IPython, and calculate the indentation for | ||||
the next line." | ||||
(interactive) | ||||
(if ipython-autoindent | ||||
(let ((line (buffer-substring (point-at-bol) (point))) | ||||
(after-prompt1) | ||||
(after-prompt2)) | ||||
(save-excursion | ||||
(comint-bol t) | ||||
(if (looking-at py-shell-input-prompt-1-regexp) | ||||
(setq after-prompt1 t) | ||||
(setq after-prompt2 (looking-at py-shell-input-prompt-2-regexp))) | ||||
(with-current-buffer (ipython-get-indenting-buffer) | ||||
(when after-prompt1 | ||||
(erase-buffer)) | ||||
(when (or after-prompt1 after-prompt2) | ||||
(delete-region (point-at-bol) (point)) | ||||
(insert line) | ||||
(newline-and-indent)))))) | ||||
;; send input line to ipython interpreter | ||||
(comint-send-input)) | ||||
(defun ipython-indentation-hook (string) | ||||
"Insert indentation string if py-shell-input-prompt-2-regexp | ||||
matches last process output." | ||||
(let* ((start-marker (or comint-last-output-start | ||||
(point-min-marker))) | ||||
(end-marker (process-mark (get-buffer-process (current-buffer)))) | ||||
(text (ansi-color-filter-apply (buffer-substring start-marker end-marker)))) | ||||
fperez
|
r339 | ;; XXX if `text' matches both pattern, it MUST be the last prompt-2 | ||
(when (and (string-match py-shell-input-prompt-2-regexp text) | ||||
(not (string-match "\n$" text))) | ||||
(with-current-buffer (ipython-get-indenting-buffer) | ||||
(setq ipython-indentation-string | ||||
(buffer-substring (point-at-bol) (point)))) | ||||
(goto-char end-marker) | ||||
(insert ipython-indentation-string) | ||||
(setq ipython-indentation-string nil)))) | ||||
fperez
|
r295 | |||
(add-hook 'py-shell-hook | ||||
(lambda () | ||||
(add-hook 'comint-output-filter-functions | ||||
'ipython-indentation-hook))) | ||||
(define-key py-shell-map (kbd "RET") 'ipython-send-and-indent) | ||||
;;; / end autoindent support | ||||
fperez
|
r0 | (provide 'ipython) | ||