Mercurial > hg > dotemacs
changeset 61:e26268005d54
Add ipython, python-mode, tabbar, and matlab
author | Jordi Gutiérrez Hermoso <jordigh@octave.org> |
---|---|
date | Mon, 30 Jul 2012 15:35:01 -0400 |
parents | 39c151907b87 |
children | d5ee4737d506 |
files | plugins/ipython.el plugins/matlab.el plugins/python-mode.el plugins/tabbar.el |
diffstat | 4 files changed, 25315 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/plugins/ipython.el @@ -0,0 +1,491 @@ +;;; 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 +;; URL: http://ipython.scipy.org +;; Compatibility: Emacs21, XEmacs21 +;; FIXME: #$@! INPUT RING +(defconst ipython-version "$Revision: 2927 $" + "VC version number.") + +;;; Commentary +;; This library makes all the functionality python-mode has when running with +;; the normal python-interpreter available for ipython, too. It also enables a +;; persistent py-shell command history across sessions (if you exit python +;; 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 +;; line only needed if the default (``"ipython"``) is wrong):: +;; +;; (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-!``). +;; +;; You can customize the arguments passed to the IPython instance at startup by +;; setting the ``py-python-command-args`` variable. For example, to start +;; always in ``pylab`` mode with hardcoded light-background colors, you can +;; use:: +;; +;; (setq py-python-command-args '("-pylab" "-colors" "LightBG")) +;; +;; +;; 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. +;; +;; +;; 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 +;; ------------------------- +;; +;; - 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``/ +;; pdbtrack combo. 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). +;; +;; - 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) +;; (define-key comint-mode-map [(meta p)] +;; 'comint-previous-matching-input-from-input) +;; (define-key comint-mode-map [(meta n)] +;; '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 +;; (<ipython-user@scipy.org>) where I (a.s.) or someone else will try to +;; answer them (it helps if you specify your emacs version, OS etc; +;; 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'") +;; +;; nor:: +;; +;; (let ((py-python-command-args (append py-python-command-args +;; '("-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) + +(defcustom ipython-command "ipython" + "*Shell command used to start ipython." + :type 'string + :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") + + +(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)) + ;; 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) + ;; 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) + ;; 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) + ;;XXX this is really just a cheap hack, it only completes symbols in the + ;;interactive session -- useful nonetheless. + (define-key py-mode-map [(meta tab)] 'ipython-complete) + + ) + (add-hook 'py-shell-hook 'ipython-shell-hook) + ;; Regular expression that describes tracebacks for IPython in context and + ;; verbose mode. + + ;;Adapt python-mode settings for ipython. + ;; (this works for %xmode 'verbose' or 'context') + + ;; XXX putative regexps for syntax errors; unfortunately the + ;; 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]+\\)" + + (setq py-traceback-line-re + "\\(^[^\t >].+?\\.py\\).*\n +[0-9]+[^\00]*?\n-+> \\([0-9]+\\)+") + + + ;; Recognize the ipython pdb, whose prompt is 'ipdb>' or 'ipydb>' + ;;instead of '(Pdb)' + (setq py-pdbtrack-input-prompt "\n[(<]*[Ii]?[Pp]y?db[>)]+ ") + (setq pydb-pydbtrack-input-prompt "\n[(]*ipydb[>)]+ ") + + (setq py-shell-input-prompt-1-regexp "^In \\[[0-9]+\\]: *" + py-shell-input-prompt-2-regexp "^ [.][.][.]+: *" ) + ;; select a suitable color-scheme + (unless (member "-colors" py-python-command-args) + (setq py-python-command-args + (nconc py-python-command-args + (list "-colors" + (cond + ((eq frame-background-mode 'dark) + "Linux") + ((eq frame-background-mode 'light) + "LightBG") + (t ; default (backg-mode isn't always set by XEmacs) + "LightBG")))))) + (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)) + + +;; 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 + to python instead of ipython." + (let ((py-which-shell (if (and (comint-check-proc "*Python*") (not async)) + py-python-command + ipython-backup-of-py-python-command))) + 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 + + In [2]: sys.stdout.write 'Hi!\n' + ------> sys.stdout.write ('Hi!\n') + Hi! + + In [3]: 3 + 4 + Out[3]: 7 + +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 + ;; replace ``In [3]: bla`` with ``>>> bla`` and + ;; ``... : 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))))) + +(defvar ipython-completion-command-string + "print ';'.join(__IP.Completer.all_completions('%s')) #PYTHON-MODE SILENT\n" + "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 ";") + (python-process (or (get-buffer-process (current-buffer)) + ;XXX hack for .py buffers + (get-process py-which-bufname))) + ;; 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 + (append comint-output-filter-functions + '(ansi-color-filter-apply + (lambda (string) + ;(message (format "DEBUG filtering: %s" string)) + (setq ugly-return (concat ugly-return string)) + (delete-region comint-last-output-start + (process-mark (get-buffer-process (current-buffer))))))))) + ;(message (format "#DEBUG pattern: '%s'" pattern)) + (process-send-string python-process + (format ipython-completion-command-string pattern)) + (accept-process-output python-process) + ;(message (format "DEBUG return: %s" ugly-return)) + (setq completions + (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 ";") + (python-process (or (get-buffer-process (current-buffer)) + ;XXX hack for .py buffers + (get-process py-which-bufname))) + ;; 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-preoutput-filter-functions + (append comint-preoutput-filter-functions + '(ansi-color-filter-apply + (lambda (string) + (setq ugly-return (concat ugly-return string)) + ""))))) + (process-send-string python-process + (format ipython-completion-command-string pattern)) + (accept-process-output python-process) + (setq completions + (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) + (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 "*IPython Completions*" + (display-completion-list (all-completions pattern completion-table))) + (message "Making completion list...%s" "done"))))) +) + +;;; 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)))) + ;; 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)))) + +(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 + +(provide 'ipython)
new file mode 100644 --- /dev/null +++ b/plugins/matlab.el @@ -0,0 +1,5584 @@ +;;; matlab.el --- major mode for MATLAB(R) dot-m files +;; +;; Author: Matt Wette <mwette@alumni.caltech.edu>, +;; Eric M. Ludlam <eludlam@mathworks.com> +;; Maintainer: Eric M. Ludlam <eludlam@mathworks.com> +;; Created: 04 Jan 91 +;; Keywords: MATLAB(R) +;; Version: + +(defconst matlab-mode-version "3.3.0" + "Current version of MATLAB(R) mode.") + +;; +;; Copyright (C) 2004-2005 The Mathworks, Inc +;; Copyright (C) 1997-2004 Eric M. Ludlam: The MathWorks, Inc +;; Copyright (C) 1991-1997 Matthew R. Wette +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;; +;;; Commentary: +;; +;; This major mode for GNU Emacs provides support for editing MATLAB(R) dot-m +;; files. It automatically indents for block structures (including nested +;; functions), line continuations (e.g., ...), and comments. +;; +;; Additional features include auto-fill including auto-additions of +;; ellipsis for commands, and even strings. Block/end construct +;; highlighting as you edit. Primitive code-verification and +;; identification. Templates and other code editing functions. +;; Advanced symbol completion. Code highlighting via font-lock. +;; There are many navigation commands that let you move across blocks +;; of code at different levels. +;; +;; Lastly, there is support for running MATLAB(R) in an Emacs buffer, +;; with full shell history and debugger support (when used with the db +;; commands.) The shell can be used as an online help while editing +;; code, providing help on functions, variables, or running arbitrary +;; blocks of code from the buffer you are editing. + +;;; Code: + +(require 'easymenu) +(require 'tempo) +(require 'derived) + +;; compatibility +(if (string-match "X[Ee]macs" emacs-version) + (progn + (defalias 'matlab-make-overlay 'make-extent) + (defalias 'matlab-overlay-put 'set-extent-property) + (defalias 'matlab-overlay-get 'extent-property) + (defalias 'matlab-delete-overlay 'delete-extent) + (defalias 'matlab-overlay-start 'extent-start-position) + (defalias 'matlab-overlay-end 'extent-end-position) + (defalias 'matlab-previous-overlay-change 'previous-extent-change) + (defalias 'matlab-next-overlay-change 'next-extent-change) + (defalias 'matlab-overlays-at + (lambda (pos) (extent-list nil pos pos))) + (defalias 'matlab-cancel-timer 'delete-itimer) + (defun matlab-run-with-idle-timer (secs repeat function &rest args) + (condition-case nil + (apply 'start-itimer + "matlab" function secs + (if repeat secs nil) t + t (car args)) + (error + ;; If the above doesn't work, then try this old version of + ;; start itimer. + (start-itimer "matlab" function secs (if repeat secs nil))))) + ) + (defalias 'matlab-make-overlay 'make-overlay) + (defalias 'matlab-overlay-put 'overlay-put) + (defalias 'matlab-overlay-get 'overlay-get) + (defalias 'matlab-delete-overlay 'delete-overlay) + (defalias 'matlab-overlay-start 'overlay-start) + (defalias 'matlab-overlay-end 'overlay-end) + (defalias 'matlab-previous-overlay-change 'previous-overlay-change) + (defalias 'matlab-next-overlay-change 'next-overlay-change) + (defalias 'matlab-overlays-at 'overlays-at) + (defalias 'matlab-cancel-timer 'cancel-timer) + (defalias 'matlab-run-with-idle-timer 'run-with-idle-timer) + ) + +(cond ((fboundp 'point-at-bol) + (defalias 'matlab-point-at-bol 'point-at-bol) + (defalias 'matlab-point-at-eol 'point-at-eol)) + ;; Emacs 20.4 + ((fboundp 'line-beginning-position) + (defalias 'matlab-point-at-bol 'line-beginning-position) + (defalias 'matlab-point-at-eol 'line-end-position)) + (t + (defmacro matlab-point-at-bol () + (save-excursion (beginning-of-line) (point))) + (defmacro matlab-point-at-eol () + (save-excursion (end-of-line) (point))))) + +(defmacro matlab-run-in-matlab-mode-only (&rest body) + "Execute BODY only if the active buffer is a MATLAB(R) M-file buffer." + `(if (eq major-mode 'matlab-mode) + (progn + ,@body) + (error "This command works only in a MATLAB M-file buffer"))) + +(defun matlab-with-emacs-link () + "Return non-nil if Emacs Link is running and user wants to use it." + (and (featurep 'matlab-eei) + matlab-use-eei + matlab-eei-process)) + +;;; User-changeable variables ================================================= + +;; Variables which the user can change +(defgroup matlab nil + "MATLAB(R) mode." + :prefix "matlab-" + :group 'languages) + +(defcustom matlab-indent-level 4 + "*The basic indentation amount in `matlab-mode'." + :group 'matlab + :type 'integer) + +(defcustom matlab-cont-level 4 + "*Basic indentation after continuation if no other methods are found." + :group 'matlab + :type 'integer) + +(defcustom matlab-cont-requires-ellipsis t + "*Specify if ellipses are required at the end of a line for continuation. +Future versions of Matlab may not require ellipses ... , so a heuristic +determining if there is to be continuation is used instead." + :group 'matlab + :type 'integer) + +(defcustom matlab-case-level '(2 . 2) + "*How far to indent case/otherwise statements in a switch. +This can be an integer, which is the distance to indent the CASE and +OTHERWISE commands, and how far to indent commands appearing in CASE +and OTHERWISE blocks. It can also be a cons cell which is of form + (CASEINDENT . COMMANDINDENT) +where CASEINDENT is the indentation of the CASE and OTHERWISE +statements, and COMMANDINDENT is the indentation of commands appearing +after the CASE or OTHERWISE command. + +Note: Currently a bug exists if: + CASEINDENT+COMMANDINDENT != `matlab-indent-level' +so if you customize these variables, follow the above rule, and you +should be ok." + :group 'matlab + :type 'sexp) + +(defcustom matlab-indent-function-body 'guess + "*If non-nil, indent body of function. +If the global value is nil, do not indent function bodies. +If the global value is t, always indent function bodies. +If the global value is 'guess, then the local value will be set to +either nil or t when the MATLAB mode is started based on the +file's current indentation." + :group 'matlab + :type '(choice (const :tag "Always" t) + (const :tag "Never" nil) + (const :tag "Guess" 'guess))) + +(make-variable-buffer-local 'matlab-indent-function-body) + +(defcustom matlab-functions-have-end nil + "*If non-nil, functions-have-end minor mode is on by default." + :group 'matlab + :type 'boolean) + +(make-variable-buffer-local 'matlab-functions-have-end) + +(defun matlab-toggle-functions-have-end () + (interactive) + (matlab-toggle-functions-have-end-minor-mode)) + +;; The following minor mode is on if and only if the above variable is true; +(easy-mmode-define-minor-mode matlab-functions-have-end-minor-mode + "Toggle functions-have-end minor mode, indicating function/end pairing." + nil + (condition-case nil ;; avoid parse error on xemacs + (eval (read "#(\" function...end\" 0 15 (face (font-lock-keyword-face) fontified t))")) + (error " function...end")) + nil ; empty mode-map +) + +(defun matlab-toggle-functions-have-end-minor-mode () + (matlab-functions-have-end-minor-mode) + (if (and matlab-functions-have-end-minor-mode (not (eq major-mode 'matlab-mode))) + (progn + (matlab-functions-have-end-minor-mode -1) + (error "functions-have-end minor mode is only for MATLAB Major mode"))) + (setq matlab-functions-have-end matlab-functions-have-end-minor-mode)) + +(defcustom matlab-indent-past-arg1-functions + "[sg]et\\(_param\\)?\\|waitfor" + "*Regex describing functions whose first arg is special. +This specialness means that all following parameters which appear on +continued lines should appear indented to line up with the second +argument, not the first argument." + :group 'matlab + :type 'string) + +(defcustom matlab-arg1-max-indent-length 15 + "*The maximum length to indent when indenting past arg1. +If arg1 is exceptionally long, then only this number of characters +will be indented beyond the open paren starting the parameter list.") + +(defcustom matlab-maximum-indents '(;; = is a convenience. Don't go too far + (?= . (10 . 4)) + ;; Fns should provide hard limits + (?\( . 50) + ;; Matrix/Cell arrays + (?\[ . 20) + (?\{ . 20)) + "Alist of maximum indentations when lining up code. +Each element is of the form (CHAR . INDENT) where char is a character +the indent engine is using, and INDENT is the maximum indentation +allowed. Indent could be of the form (MAXIMUM . INDENT), where +MAXIMUM is the maximum allowed calculated indent, and INDENT is the +amount to use if MAXIMUM is reached." + :group 'matlab + :type '(repeat (cons (character :tag "Open List Character") + (sexp :tag "Number (max) or cons (max indent)")))) + +(defcustom matlab-handle-simulink t + "*If true, add in a few simulink customizations. +This variable's state is mostly useful when set at load time when +simulink font lock keywords can be removed. This will handle +additional cases as the need arrises." + :group 'matlab + :type 'boolean) + +(defcustom matlab-auto-fill t + "*If true, set variable `auto-fill-function' to our function at startup." + :group 'matlab + :type 'boolean) + +(defcustom matlab-fill-fudge 10 + "Number of characters around `fill-column' we can fudge filling. +Basically, there are places that are very convenient to fill at, but +might not be the closest fill spot, or occur after `fill-column'. +If they occur within this fudge factor, we will use them. +Also, if none of the above occur, and we find a symbol to break at, +but an open paren (group) starts or ends within this fudge factor, +move there to boost the amount of fill leverage we can get." + :group 'matlab + :type 'integer) + +(defcustom matlab-fill-fudge-hard-maximum 79 + "The longest line allowed when auto-filling code. +This overcomes situations where the `fill-column' plus the +`matlab-fill-fudge' is greater than some hard desired limit." + :group 'matlab + :type 'integer) + +(defcustom matlab-elipsis-string "..." + "Text used to perform continuation on code lines. +This is used to generate and identify continuation lines.") + +(defcustom matlab-fill-code t + "*If true, `auto-fill-mode' causes code lines to be automatically continued." + :group 'matlab + :type 'boolean) + +(defcustom matlab-fill-count-ellipsis-flag t + "*Non-nil means to count the ellipsis when auto filling. +This effectively shortens the `fill-column' by the length of +`matlab-elipsis-string'.") + +(defcustom matlab-fill-strings-flag t + "*Non-nil means that when auto-fill is on, strings are broken across lines. +If `matlab-fill-count-ellipsis-flag' is non nil, this shortens the +`fill-column' by the length of `matlab-elipsis-string'.") + +(defcustom matlab-comment-column 40 + "*The goal comment column in `matlab-mode' buffers." + :group 'matlab + :type 'integer) + +(defcustom matlab-comment-anti-indent 0 + "*Amount of anti-indentation to use for comments in relation to code." + :group 'matlab + :type 'integer) + +(defcustom matlab-comment-line-s "% " + "*String to start comment on line by itself." + :group 'matlab + :type 'string) + +(defcustom matlab-comment-on-line-s "% " + "*String to start comment on line with code." + :group 'matlab + :type 'string) + +(defcustom matlab-comment-region-s "% $$$ " + "*String inserted by \\[matlab-comment-region] at start of each line in \ +region." + :group 'matlab + :type 'string) + +(defcustom matlab-verify-on-save-flag t + "*Non-nil means to verify M whenever we save a file." + :group 'matlab + :type 'boolean) + +(defcustom matlab-mode-verify-fix-functions + '(matlab-mode-vf-functionname) + "List of function symbols which perform a verification and fix to M code. +Each function gets no arguments, and returns nothing. They can move +point, but it will be restored for them." + :group 'matlab + :type '(repeat (choice :tag "Function: " + '(matlab-mode-vf-functionname + matlab-mode-vf-block-matches-forward + matlab-mode-vf-block-matches-backward + matlab-mode-vf-quiesce-buffer + )))) + +(defcustom matlab-block-verify-max-buffer-size 50000 + "*Largest buffer size allowed for block verification during save." + :group 'matlab + :type 'integer) + +;; It is time to disable this. +(defcustom matlab-vers-on-startup nil + "*If non-nil, show the version number on startup." + :group 'matlab + :type 'boolean) + +(defcustom matlab-highlight-block-match-flag t + "*Non-nil means to highlight the matching if/end/whatever. +The highlighting only occurs when the cursor is on a block start or end +keyword." + :group 'matlab + :type 'boolean) + +(defcustom matlab-show-periodic-code-details-flag nil + "*Non-nil means to show code details in the minibuffer. +This will only work if `matlab-highlight-block-match-flag' is non-nil." + :group 'matlab + :type 'boolean) + +(defcustom matlab-use-eei t + "*Use Emacs Link for save-and-go and run-region." + :group 'matlab + :type 'boolean) + +(defcustom matlab-mode-hook nil + "*List of functions to call on entry to MATLAB mode." + :group 'matlab + :type 'hook) + +(defcustom matlab-completion-technique 'complete + "*How the `matlab-complete-symbol' interfaces with the user. +Valid values are: + +'increment - which means that new strings are tried with each + successive call until all methods are exhausted. + (Similar to `hippie-expand'.) +'complete - Which means that if there is no single completion, then + all possibilities are displayed in a completion buffer." + :group 'matlab + :type '(radio (const :tag "Incremental completion (hippie-expand)." + increment) + (const :tag "Show completion buffer." + complete))) + +(defcustom matlab-show-mlint-warnings nil + "*If non-nil, show mlint warnings." + :group 'matlab + :type 'boolean) + +(make-variable-buffer-local 'matlab-show-mlint-warnings) + +(defcustom matlab-highlight-cross-function-variables nil + "*If non-nil, highlight cross-function variables." + :group 'matlab + :type 'boolean) + +(make-variable-buffer-local 'matlab-highlight-cross-function-variables) + +(defcustom matlab-return-add-semicolon nil + "*If non nil, check to see a semicolon is needed when RET is pressed." + :group 'matlab + :type 'boolean) + +(make-variable-buffer-local 'matlab-return-add-semicolon) + +;; Load in the region we use for highlighting stuff. +(if (and (featurep 'custom) (fboundp 'custom-declare-variable)) + + (let ((l-region-face (if (facep 'region) 'region 'zmacs-region))) + ;; If we have custom, we can make our own special face like this + (defface matlab-region-face + (list + (list t + (list :background (face-background l-region-face) + :foreground (face-foreground l-region-face)))) + "*Face used to highlight a matlab region." + :group 'matlab)) + + ;; If we do not, then we can fake it by copying 'region. + (cond ((facep 'region) + (copy-face 'region 'matlab-region-face)) + (t + (copy-face 'zmacs-region 'matlab-region-face)))) + +(defvar matlab-unterminated-string-face 'matlab-unterminated-string-face + "Self reference for unterminated string face.") + +(defvar matlab-simulink-keyword-face 'matlab-simulink-keyword-face + "Self reference for simulink keywords.") + +(defvar matlab-nested-function-keyword-face 'matlab-nested-function-keyword-face + "Self reference for nested function/end keywords.") + +(defvar matlab-cross-function-variable-face 'matlab-cross-function-variable-face + "Self reference for cross-function variables.") + +(defvar matlab-cellbreak-face 'matlab-cellbreak-face + "Self reference for cellbreaks.") + +(defun matlab-font-lock-adjustments () + "Make adjustments for font lock. +If font lock is not loaded, lay in wait." + (if (and (featurep 'custom) (fboundp 'custom-declare-variable)) + + (progn + (defface matlab-unterminated-string-face + (list + (list t + (list :background (face-background font-lock-string-face) + :foreground (face-foreground font-lock-string-face) + :underline t))) + "*Face used to highlight unterminated strings." + :group 'matlab) + (defface matlab-simulink-keyword-face + (list + (list t + (list :background (face-background font-lock-type-face) + :foreground (face-foreground font-lock-type-face) + :underline t))) + "*Face used to highlight simulink specific functions." + :group 'matlab) + (defface matlab-nested-function-keyword-face + (list + (list t + (list :slant 'italic))) + "*Face to use for cross-function variables.") + (defface matlab-cross-function-variable-face + (list + (list t + (list :weight 'bold + :slant 'italic))) + "*Face to use for cross-function variables." + :group 'matlab) + (defface matlab-cellbreak-face + (list + (list t + (list :background (face-background font-lock-comment-face) + :foreground (face-foreground font-lock-comment-face) + :overline t + :bold t))) + "*Face to use for cellbreak %% lines.") + ) + + ;; Now, lets make the unterminated string face + (cond ((facep 'font-lock-string-face) + (copy-face 'font-lock-string-face + 'matlab-unterminated-string-face)) + (t + (make-face 'matlab-unterminated-string-face))) + (set-face-underline-p 'matlab-unterminated-string-face t) + + ;; Now make some simulink faces + (cond ((facep 'font-lock-type-face) + (copy-face 'font-lock-type-face 'matlab-simulink-keyword-face)) + (t + (make-face 'matlab-simulink-keyword-face))) + (set-face-underline-p 'matlab-simulink-keyword-face t) + + ;; Now make some nested function/end keyword faces + (cond ((facep 'font-lock-type-face) + (copy-face 'font-lock-type-face 'matlab-nested-function-keyword-face)) + (t + (make-face 'matlab-nested-function-keyword-face))) + + ;; Now make some cross-function variable faces + (cond ((facep 'font-lock-type-face) + (copy-face 'font-lock-type-face 'matlab-cross-function-variable-face)) + (t + (make-face 'matlab-cross-function-variable-face))) + (set-face-bold-p 'matlab-cross-function-variable-face t) + + ;; Now make some cellbreak variable faces + (cond ((facep 'font-comment-face) + (copy-face 'font-lock-comment-face 'matlab-cellbreak-face)) + (t + (make-face 'matlab-cellbreak-face))) + (set-face-bold-p 'matlab-cellbreak-face t) + (condition-case nil + (set-face-attribute 'matlab-cellbreak-face nil :overline t) + (error nil)) + ) + (remove-hook 'font-lock-mode-hook 'matlab-font-lock-adjustments)) + +;; Make the adjustments for font lock after it's loaded. +;; I found that eval-after-load was unreliable. +(if (featurep 'font-lock) + (matlab-font-lock-adjustments) + (add-hook 'font-lock-mode-hook 'matlab-font-lock-adjustments)) + + +;;; MATLAB mode variables ===================================================== + +(defvar matlab-tempo-tags nil + "List of templates used in MATLAB mode.") + +;; syntax table +(defvar matlab-mode-syntax-table + (let ((st (make-syntax-table (standard-syntax-table)))) + (modify-syntax-entry ?_ "_" st) + (modify-syntax-entry ?% "<" st) + (modify-syntax-entry ?\n ">" st) + (modify-syntax-entry ?\\ "." st) + (modify-syntax-entry ?\t " " st) + (modify-syntax-entry ?+ "." st) + (modify-syntax-entry ?- "." st) + (modify-syntax-entry ?* "." st) + (modify-syntax-entry ?' "." st) + (modify-syntax-entry ?/ "." st) + (modify-syntax-entry ?= "." st) + (modify-syntax-entry ?< "." st) + (modify-syntax-entry ?> "." st) + (modify-syntax-entry ?& "." st) + (modify-syntax-entry ?| "." st) + st) + "The syntax table used in `matlab-mode' buffers.") + +(defvar matlab-mode-special-syntax-table + (let ((st (copy-syntax-table matlab-mode-syntax-table))) + ;; Make _ a part of words so we can skip them better + (modify-syntax-entry ?_ "w" st) + st) + "The syntax table used when navigating blocks.") + +;; abbrev table +(defvar matlab-mode-abbrev-table nil + "The abbrev table used in `matlab-mode' buffers.") + +(define-abbrev-table 'matlab-mode-abbrev-table ()) + +;;; Keybindings =============================================================== + +(defvar matlab-help-map + (let ((km (make-sparse-keymap))) + (define-key km "r" 'matlab-shell-run-command) + (define-key km "f" 'matlab-shell-describe-command) + (define-key km "a" 'matlab-shell-apropos) + (define-key km "v" 'matlab-shell-describe-variable) + (define-key km "t" 'matlab-shell-topic-browser) + km) + "The help key map for `matlab-mode' and `matlab-shell-mode'.") + +(defvar matlab-insert-map + (let ((km (make-sparse-keymap))) + (define-key km "c" 'matlab-insert-next-case) + (define-key km "e" 'matlab-insert-end-block) + (define-key km "i" 'tempo-template-matlab-if) + (define-key km "I" 'tempo-template-matlab-if-else) + (define-key km "f" 'tempo-template-matlab-for) + (define-key km "s" 'tempo-template-matlab-switch) + (define-key km "t" 'tempo-template-matlab-try) + (define-key km "w" 'tempo-template-matlab-while) + (define-key km "F" 'tempo-template-matlab-function) + (define-key km "'" 'matlab-stringify-region) + ;; Not really inserts, but auto coding stuff + (define-key km "\C-s" 'matlab-ispell-strings) + (define-key km "\C-c" 'matlab-ispell-comments) + km) + "Keymap used for inserting simple texts based on context.") + +;; mode map +(defvar matlab-mode-map + (let ((km (make-sparse-keymap))) + (define-key km [return] 'matlab-return) + (define-key km "%" 'matlab-electric-comment) + (define-key km "\C-c;" 'matlab-comment-region) + (define-key km "\C-c:" 'matlab-uncomment-region) + (define-key km [(control c) return] 'matlab-comment-return) + (define-key km [(control c) (control c)] matlab-insert-map) + (define-key km [(control c) (control f)] 'matlab-fill-comment-line) + (define-key km [(control c) (control j)] 'matlab-justify-line) + (define-key km [(control c) (control q)] 'matlab-fill-region) + (define-key km [(control c) (control s)] 'matlab-shell-save-and-go) + (define-key km [(control c) (control r)] 'matlab-shell-run-region) + (define-key km [(meta control return)] 'matlab-shell-run-cell) + (define-key km [(control c) (control t)] 'matlab-show-line-info) + (define-key km [(control c) ?. ] 'matlab-find-file-on-path) + (define-key km [(control h) (control m)] matlab-help-map) + (define-key km [(control j)] 'matlab-linefeed) + (define-key km "\M-\r" 'newline) + (define-key km [(meta \;)] 'matlab-comment) + (define-key km [(meta q)] 'matlab-fill-paragraph) + (define-key km [(meta a)] 'matlab-beginning-of-command) + (define-key km [(meta e)] 'matlab-end-of-command) + (define-key km [(meta j)] 'matlab-comment-line-break-function) + (define-key km [(meta s)] 'matlab-show-matlab-shell-buffer) + (define-key km "\M-\t" 'matlab-complete-symbol) + (define-key km [(meta control f)] 'matlab-forward-sexp) + (define-key km [(meta control b)] 'matlab-backward-sexp) + (define-key km [(meta control q)] 'matlab-indent-sexp) + (define-key km [(meta control a)] 'matlab-beginning-of-defun) + (define-key km [(meta control e)] 'matlab-end-of-defun) + (if (string-match "XEmacs" emacs-version) + (define-key km [(control meta button1)] 'matlab-find-file-click) + (define-key km [(control meta mouse-2)] 'matlab-find-file-click)) + (substitute-key-definition 'comment-region 'matlab-comment-region + km) ; global-map ;torkel + km) + "The keymap used in `matlab-mode'.") + +;;; Font locking keywords ===================================================== + +(defvar matlab-string-start-regexp "\\(^\\|[^]})a-zA-Z0-9_.']\\)" + "Regexp used to represent the character before the string char '. +The ' character has restrictions on what starts a string which is needed +when attempting to understand the current context.") + +;; To quote a quote, put two in a row, thus we need an anchored +;; first quote. In addition, we don't want to color strings in comments. +(defvar matlab-string-end-regexp "[^'\n]*\\(''[^'\n]*\\)*'" + "Regexp used to represent the character pattern for ending a string. +The ' character can be used as a transpose, and can transpose transposes. +Therefore, to end, we must check all that goop.") + +(defun matlab-font-lock-string-match-normal (limit) + "When font locking strings, call this function for normal strings. +Argument LIMIT is the maximum distance to scan." + (matlab-font-lock-string-match-here + (concat matlab-string-start-regexp + "\\('" matlab-string-end-regexp "\\)" + "\\([^']\\|$\\)") + limit)) + +(defun matlab-font-lock-string-match-unterminated (limit) + "When font locking strings, call this function for normal strings. +Argument LIMIT is the maximum distance to scan." + (matlab-font-lock-string-match-here + (concat matlab-string-start-regexp "\\('[^'\n]*\\(''[^'\n]*\\)*\\)$") + limit)) + +(defun matlab-font-lock-string-match-here (regex limit) + "When font-locking strings, call this function to determine a match. +Argument REGEX is the expression to scan for. Match 2 must be the string. +Argument LIMIT is the maximum distance to scan." + (let (e) + (while (and (re-search-forward regex limit t) + (progn + ;; This gets us out of a comment after the string. + (setq e (match-end 2)) + (goto-char (match-beginning 2)) + (prog1 + (or (matlab-cursor-in-comment) + (if (bolp) nil + (save-excursion + (forward-char -1) + (matlab-cursor-in-string)))) + (goto-char e)))) + (setq e nil)) + (if (not e) + nil + (goto-char e) + t))) + +(defun matlab-font-lock-comment-match (limit) + "When font-locking comments, call this function to determine a match. +Argument LIMIT is the maximum distance to scan." + (let (e) + (while (and (re-search-forward "\\(%[^%\n]*\\)" limit t) + (progn + (setq e (match-end 1)) + (member (get-text-property (match-beginning 0) 'face) + '(font-lock-string-face + matlab-unterminated-string-face)))) + (setq e nil)) + (if (not e) + nil + (goto-char e) + t))) + +(defun matlab-find-unreachable-code (limit) + "Find code that is if'd out with if(0) or if(false), and mark it as a comment. +The if(0) and else/end construct should be highlighted differently. +Argument LIMIT is the maximum distance to search." + (if (and (< (point) limit) + (re-search-forward + "\\<\\(if\\>\\s-*(?\\s-*\\(0\\|false\\)\\s-*)?$\\)" + limit t)) + (let ((b1 (match-beginning 1)) + (e1 (match-end 1)) + (b2 nil) (e2 nil) + (b3 nil) (e3 nil)) + (goto-char b1) + (condition-case nil + (progn + ;; Go forward over the matlab sexp. Include scanning + ;; for ELSE since parts of the ELSE block are not + ;; `commented out'. + (matlab-forward-sexp t) + (forward-word -1) + ;; Is there an ELSE in this block? + (if (looking-at (matlab-block-mid-re)) + (progn + (setq b3 (match-beginning 0) + e3 (match-end 0)) + ;; Now find the REAL end. + (matlab-forward-sexp) + (forward-word -1))) + ;; End of block stuff + (if (looking-at (matlab-block-end-re)) + (progn + (setq b2 (match-beginning 0) + e2 (match-end 0)) + ;; make sure something exists... + (if (not b3) (setq b3 b2 e3 e2))) + (error "Eh?")) + ;; Ok, build up some match data. + (set-match-data + (list b1 e2 ;the real deal. + b1 e1 ;if (0) + b2 e2 ;end + b3 e3 ;else (if applicable.) + b1 e3)) ;body commented out. + t) + (error nil))))) + +(defun matlab-font-lock-nested-function-keyword-match (limit) + "Find next nested function/end keyword for font-lock. +Argument LIMIT is the maximum distance to search." +; Because of the way overlays are setup, the cursor will be sitting +; on either a "function" or "end" keyword. + (catch 'result + (let ((pos (point)) + overlays) + (while (< pos limit) + (setq overlays (matlab-overlays-at pos)) + (while overlays + (let ((overlay (car overlays))) + (when (matlab-overlay-get overlay 'nested-function) + (when (= pos (matlab-overlay-start overlay)) + (goto-char pos) + ;; The following line presumably returns true. + (throw 'result (re-search-forward "function" (+ pos 8) t))) + (let ((end-of-overlay (- (matlab-overlay-end overlay) 3))) + (when (<= pos end-of-overlay) + (goto-char end-of-overlay) + (throw 'result + (re-search-forward "end" (+ end-of-overlay 3) t)))))) + (setq overlays (cdr overlays))) + (setq pos (matlab-next-overlay-change pos))) + nil ;; no matches, stop + ))) + +(defun matlab-font-lock-cross-function-variables-match (limit) + "Find next cross-function variable for font-lock. +Argument LIMIT is the maximum distance to search." + (catch 'result + (let ((pos (point)) + overlays variables) + (while (< pos limit) + (let ((overlays (matlab-overlays-at pos))) + (while overlays + (let ((overlay (car overlays))) + (setq variables (matlab-overlay-get + overlay 'cross-function-variables)) + (if variables + (progn + (goto-char pos) + (setq pos (min limit (matlab-overlay-end overlay))) + (if (re-search-forward variables pos t) + (progn + (throw 'result t)))))) + (setq overlays (cdr overlays)))) + (setq pos (matlab-next-overlay-change pos))) + nil ;; no matches, stop + ))) + +(defun matlab-find-block-comments (limit) + "Find code that is commented out with %{ until %}. +Argument LIMIT is the maximum distance to search." + (if (and (< (point) limit) + (re-search-forward "%{" limit t)) + (let ((b1 (match-beginning 0)) + (e1 (match-end 0)) + (b2 nil) (e2 nil) + (b3 nil) (e3 nil)) + (goto-char b1) + (forward-char -1) + (when (not (matlab-cursor-in-comment)) + (setq b2 (re-search-forward "%}" limit t)) + (when b2 + (setq b2 (match-beginning 0) + e2 (match-end 0)) + (set-match-data + (list b1 e2 ; full match + b1 e2 ; the full comment + b1 e1 ; the block start + b2 e2 ; the block end + )) + t + ))))) + +(defcustom matlab-keyword-list '("global" "persistent" "for" "parfor" "while" + "spmd" "if" "elseif" "else" + "endfunction" "return" "break" "continue" + "switch" "case" "otherwise" "try" + "catch" "tic" "toc" + ;; MCOS keywords + "classdef" "properties" "methods" "enumeration" + ) + "List of keywords for MATLAB used in highlighting. +Customizing this variable is only useful if `regexp-opt' is available." + :group 'matlab + :type '(repeat (string :tag "Keyword: "))) + +(defcustom matlab-handle-graphics-list '("figure" "axes" "axis" "line" + "surface" "patch" "text" "light" + "image" "set" "get" "uicontrol" + "uimenu" "uitoolbar" + "uitoggletool" "uipushtool" + "uicontext" "uicontextmenu" + "setfont" "setcolor") + "List of handle graphics functions used in highlighting. +Customizing this variable is only useful if `regexp-opt' is available." + :group 'matlab + :type '(repeat (string :tag "HG Keyword: "))) + +(defcustom matlab-debug-list '("dbstop" "dbclear" "dbcont" "dbdown" "dbmex" + "dbstack" "dbstatus" "dbstep" "dbtype" "dbup" + "dbquit") + "List of debug commands used in highlighting. +Customizing this variable is only useful if `regexp-opt' is available." + :group 'matlab + :type '(repeat (string :tag "Debug Keyword: "))) + +;; font-lock keywords +(defvar matlab-font-lock-keywords + (list + ;; String quote chars are also used as transpose, but only if directly + ;; after characters, numbers, underscores, or closing delimiters. + '(matlab-font-lock-string-match-normal 2 font-lock-string-face) + ;; A string with no termination is not currently highlighted. + ;; This will show that the string needs some attention. + '(matlab-font-lock-string-match-unterminated + 2 matlab-unterminated-string-face) + ;; Comments must occur after the string, that way we can check to see + ;; if the comment start char has occurred inside our string. (EL) + '(matlab-font-lock-comment-match 1 font-lock-comment-face) + ;; Various pragmas should be in different colors. + ;; I think pragmas are always lower case? + '("%#\\([a-z]+\\)" (1 'bold prepend)) + ;; General keywords + (list + (if (fboundp 'regexp-opt) + (concat "\\<\\(" (regexp-opt matlab-keyword-list) "\\)\\>") + ;; Original hard-coded value for pre Emacs 20.1 + "\\<\\(break\\|ca\\(se\\|tch\\)\\|e\\(lse\\(\\|if\\)\\|ndfunction\\)\ +\\|\\(par\\)?for\\|spmd\\|global\\|if\\|otherwise\\|return\\|switch\\|try\\|while\\|tic\\|toc\\)\\>") + '(0 font-lock-keyword-face)) + ;; The end keyword is only a keyword when not used as an array + ;; dereferencing part. + '("\\(^\\|[;,]\\)[ \t]*\\(end\\)\\b" + 2 (if (matlab-valid-end-construct-p) font-lock-keyword-face nil)) + ;; How about unreachable code? MUsT BE AFTER KEYWORDS in order to + ;; get double-highlighting. + '(matlab-find-unreachable-code + (1 'underline prepend) ;if part + (2 'underline prepend) ;end part + (3 'underline prepend) ;else part (if applicable) + (4 font-lock-comment-face prepend) ;commented out part. + ) + ;; block comments need to be commented out too! + '(matlab-find-block-comments + (1 font-lock-comment-face prepend) ; commented out + (2 'underline prepend) + (3 'underline prepend) ;the comment parts + ) + ;; Cell mode breaks get special treatment + '("^\\s-*\\(%%[^\n]*\n\\)" (1 matlab-cellbreak-face append)) + ;; Highlight cross function variables + '(matlab-font-lock-cross-function-variables-match + (1 matlab-cross-function-variable-face prepend)) + ;; Highlight nested function/end keywords + '(matlab-font-lock-nested-function-keyword-match + (0 matlab-nested-function-keyword-face prepend)) + ;; The global keyword defines some variables. Mark them. + '("^\\s-*global\\s-+" + ("\\(\\w+\\)\\(\\s-*=[^,; \t\n]+\\|[, \t;]+\\|$\\)" + nil nil (1 font-lock-variable-name-face))) + ;; Handle graphics stuff + (list + (if (fboundp 'regexp-opt) + (concat "\\<\\(" (regexp-opt matlab-handle-graphics-list) "\\)\\>") + ;; The original regular expression for pre Emacs 20.1 + "\\<\\(ax\\(es\\|is\\)\\|figure\\|get\\|image\\|li\\(ght\\|ne\\)\\|\ +patch\\|s\\(et\\(\\|color\\|font\\)\\|urface\\)\\|text\\|\ +ui\\(cont\\(ext\\(\\|menu\\)\\|rol\\)\\|menu\\|\ +\\(toggle\\|push\\)tool\\|toolbar\\)\\)\\>") + '(0 font-lock-type-face)) + ) + "Expressions to highlight in MATLAB mode.") + +(defconst matlab-function-arguments + "\\(([^)]*)\\)?\\s-*\\([,;\n%]\\|$\\)") + +(defvar matlab-gaudy-font-lock-keywords + (append + matlab-font-lock-keywords + (list + ;; defining a function, a (possibly empty) list of assigned variables, + ;; function name, and an optional (possibly empty) list of input variables + (list (concat "^\\s-*\\(function\\)\\>[ \t\n.]*" + "\\(\\[[^]]*\\]\\|\\sw+\\)[ \t\n.]*" + "=[ \t\n.]*\\(\\sw+\\)[ \t\n.]*" + matlab-function-arguments) + '(1 font-lock-keyword-face append) + '(2 font-lock-variable-name-face append) + '(3 font-lock-function-name-face append)) + ;; defining a function, a function name, and an optional (possibly + ;; empty) list of input variables + (list (concat "^\\s-*\\(function\\)[ \t\n.]+" + "\\(\\sw+\\)[ \t\n.]*" + matlab-function-arguments) + '(1 font-lock-keyword-face append) + '(2 font-lock-function-name-face append)) + ;; Anchor on the function keyword, highlight params + (list (concat "^\\s-*function\\>[ \t\n.]*" + "\\(\\(\\[[^]]*\\]\\|\\sw+\\)[ \t\n.]*=[ \t\n.]*\\)?" + "\\sw+\\s-*(") + '("\\s-*\\(\\sw+\\)\\s-*[,)]" nil nil + (1 font-lock-variable-name-face))) + ;; I like variables for FOR loops + '("\\<\\(for\\|parfor\\)\\s-+\\(\\sw+\\)\\s-*=\\s-*\ +\\(\\([^\n,;%(]+\\|([^\n%)]+)\\)+\\)" + (1 font-lock-keyword-face) + (2 font-lock-variable-name-face append) + (3 font-lock-reference-face append)) + ;; Items after a switch statements are cool + '("\\<\\(case\\|switch\\)\\s-+\\({[^}\n]+}\\|[^,%\n]+\\)" + (1 font-lock-keyword-face) (2 font-lock-reference-face)) + ;; How about a few matlab constants such as pi, infinity, and sqrt(-1)? + ;; The ^>> is in case I use this in an interactive mode someday + '("\\<\\(eps\\|pi\\|inf\\|Inf\\|NaN\\|nan\\|ans\\|i\\|j\\|^>>\\)\\>" + 1 font-lock-reference-face) + '("\\<[0-9]\\.?\\(i\\|j\\)\\>" 1 font-lock-reference-face) + ;; Define these as variables since this is about as close + ;; as matlab gets to variables + (list (concat "\\<" matlab-indent-past-arg1-functions "\\s-*") + '("(\\s-*\\(\\w+\\)\\s-*\\(,\\|)\\)" nil nil + (1 font-lock-variable-name-face))) + )) + "Expressions to highlight in MATLAB mode.") + +(defvar matlab-really-gaudy-font-lock-keywords + (append + matlab-gaudy-font-lock-keywords + (list + ;; Since it's a math language, how bout dem symbols? + '("\\([<>~]=?\\|\\.[/*^']\\|==\\|\\<xor\\>\\|[-!^&|*+\\/~:]\\)" + 1 font-lock-type-face) + ;; How about references in the HELP text. + (list (concat "^" matlab-comment-line-s "\\s-*" + "\\(\\([A-Z]+\\s-*=\\s-+\\|\\[[^]]+]\\s-*=\\s-+\\|\\)" + "\\([A-Z][0-9A-Z]+\\)\\(([^)\n]+)\\| \\)\\)") + '(1 font-lock-reference-face prepend)) + (list (concat "^" matlab-comment-line-s "\\s-*" + "See also\\s-+") + '("\\([A-Z][A-Z0-9]+\\)\\([,.]\\| and\\|$\\) *" nil nil + (1 font-lock-reference-face prepend))) + (list (concat "^" matlab-comment-line-s "\\s-*" + "\\(\\$" "Revision" "[^\n$]+\\$\\)") + '(1 font-lock-reference-face prepend)) + ;; continuation ellipsis. + '("[^.]\\(\\.\\.\\.+\\)\\([^\n]*\\)" (1 'underline) + (2 font-lock-comment-face)) + ;; How about debugging statements? + ;;'("\\<\\(db\\sw+\\)\\>" 1 'bold) + (list + (if (fboundp 'regexp-opt) + (concat "\\<\\(" (regexp-opt matlab-debug-list) "\\)\\>") + ;; pre-regexp-opt days. + "\\<\\(db\\(c\\(lear\\|ont\\)\\|down\\|mex\\|quit\\|\ +st\\(a\\(ck\\|tus\\)\\|ep\\|op\\)\\|type\\|up\\)\\)\\>") + '(0 'bold))) + (if matlab-handle-simulink + ;; Simulink functions, but only if the user wants it. + (list (list (concat "\\<\\(\\([sg]et_param\\|sim\\([gs]et\\)?\\|" + "\\(mld\\|ss\\)[A-Z]\\w+\\)\\|" + "\\(new\\|open\\|close\\|save\\|find\\)_system\\|" + "\\(add\\|delete\\|replace\\)_\\(block\\|line\\)\\|" + "simulink\\|bd\\(root\\|close\\)" + "\\)\\>") + 1 matlab-simulink-keyword-face)) + nil)) + "Expressions to highlight in MATLAB mode.") + +(defvar matlab-shell-font-lock-keywords + (list + ;; How about Errors? + '("^\\(Error in\\|Syntax error in\\)\\s-+==>\\s-+\\(.+\\)$" + (1 font-lock-comment-face) (2 font-lock-string-face)) + ;; and line numbers + '("^\\(On line [0-9]+\\)" 1 font-lock-comment-face) + ;; User beep things + '("\\(\\?\\?\\?[^\n]+\\)" 1 font-lock-comment-face) + ;; Useful user commands, but not useful programming constructs + '("\\<\\(demo\\|whatsnew\\|info\\|subscribe\\|help\\|doc\\|lookfor\\|what\ +\\|whos?\\|cd\\|clear\\|load\\|save\\|helpdesk\\|helpwin\\)\\>" + 1 font-lock-keyword-face) + ;; Various notices + '(" M A T L A B " 0 'underline) + '("All Rights Reserved" 0 'italic) + '("\\((c)\\s-+Copyright[^\n]+\\)" 1 font-lock-comment-face) + '("\\(Version\\)\\s-+\\([^\n]+\\)" + (1 font-lock-function-name-face) (2 font-lock-variable-name-face)) + ) + "Additional keywords used by MATLAB when reporting errors in interactive\ +mode.") + +;; Imenu support. +(defvar matlab-imenu-generic-expression + '((nil "^\\s-*function\\>[ \t\n.]*\\(\\(\\[[^]]*\\]\\|\\sw+\\)[ \t\n.]*\ +< =\[ \t\n.]*\\)?\\([a-zA-Z0-9_]+\\)" 3)) + "Expressions which find function headings in MATLAB M files.") + + +;;; MATLAB mode entry point ================================================== + +;; Debian change by Peter Galbraith: Customize `matlab-auto-mode' instead. +;; ;;;###autoload +;; (add-to-list 'auto-mode-alist '("\\.m$" . matlab-mode)) + +;;;###autoload +(defun matlab-mode () + "MATLAB(R) mode is a major mode for editing MATLAB dot-m files. +\\<matlab-mode-map> +Convenient editing commands are: + \\[matlab-comment-region] - Comment/Uncomment out a region of code. + \\[matlab-fill-comment-line] - Fill the current comment line. + \\[matlab-fill-region] - Fill code and comments in region. + \\[matlab-fill-paragraph] - Refill the current command or comment. + \\[matlab-complete-symbol] - Symbol completion of matlab symbols\ +based on the local syntax. + \\[matlab-indent-sexp] - Indent syntactic block of code. + +Convenient navigation commands are: + \\[matlab-beginning-of-command] - Move to the beginning of a command. + \\[matlab-end-of-command] - Move to the end of a command. + \\[matlab-beginning-of-defun] - Move to the beginning of a function. + \\[matlab-end-of-defun] - Move do the end of a function. + \\[matlab-forward-sexp] - Move forward over a syntactic block of code. + \\[matlab-backward-sexp] - Move backwards over a syntactic block of code. + +Convenient template insertion commands: + \\[tempo-template-matlab-function] - Insert a function definition. + \\[tempo-template-matlab-if] - Insert an IF END block. + \\[tempo-template-matlab-for] - Insert a FOR END block. + \\[tempo-template-matlab-switch] - Insert a SWITCH END statement. + \\[matlab-insert-next-case] - Insert the next CASE condition in a SWITCH. + \\[matlab-insert-end-block] - Insert a matched END statement. With \ +optional ARG, reindent. + \\[matlab-stringify-region] - Convert plaintext in region to a string \ +with correctly quoted chars. + +Variables: + `matlab-indent-level' Level to indent blocks. + `matlab-cont-level' Level to indent continuation lines. + `matlab-cont-requires-ellipsis' Does your MATLAB support implied elipsis. + `matlab-case-level' Level to unindent case statements. + `matlab-indent-past-arg1-functions' + Regexp of functions to indent past the first + argument on continuation lines. + `matlab-maximum-indents' List of maximum indents during lineups. + `matlab-comment-column' Goal column for on-line comments. + `fill-column' Column used in auto-fill. + `matlab-indent-function-body' If non-nil, indents body of MATLAB functions. + `matlab-functions-have-end' If non-nil, MATLAB functions terminate with end. + `matlab-return-function' Customize RET handling with this function. + `matlab-auto-fill' Non-nil, do auto-fill at startup. + `matlab-fill-code' Non-nil, auto-fill code. + `matlab-fill-strings' Non-nil, auto-fill strings. + `matlab-verify-on-save-flag' Non-nil, enable code checks on save. + `matlab-highlight-block-match-flag' + Enable matching block begin/end keywords. + `matlab-vers-on-startup' If t, show version on start-up. + `matlab-handle-simulink' If t, enable simulink keyword highlighting. + +All Key Bindings: +\\{matlab-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map matlab-mode-map) + (setq major-mode 'matlab-mode) + (setq mode-name "MATLAB") + (if (boundp 'whitespace-modes) + (add-to-list 'whitespace-modes 'matlab-mode)) + (setq local-abbrev-table matlab-mode-abbrev-table) + (set-syntax-table matlab-mode-syntax-table) + (setq indent-tabs-mode nil) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'matlab-indent-line) + (make-local-variable 'paragraph-start) + (setq paragraph-start (concat "^$\\|" page-delimiter)) + (make-local-variable 'paragraph-separate) + (setq paragraph-separate paragraph-start) + (make-local-variable 'paragraph-ignore-fill-prefix) + (setq paragraph-ignore-fill-prefix t) + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "%\\s-+") + (make-local-variable 'comment-start) + (setq comment-start "%") + (make-local-variable 'page-delimiter) + (setq page-delimiter "^\\(\f\\|%% \\)") + (make-local-variable 'comment-column) + (setq comment-column matlab-comment-column) + (make-local-variable 'comment-indent-function) + (setq comment-indent-function 'matlab-comment-indent) + (make-local-variable 'add-log-current-defun-function) + (setq add-log-current-defun-function 'matlab-current-defun) + (make-local-variable 'fill-column) + (setq fill-column default-fill-column) + (make-local-variable 'auto-fill-function) + (if matlab-auto-fill (setq auto-fill-function 'matlab-auto-fill)) + ;; Emacs 20 supports this variable. This lets users turn auto-fill + ;; on and off and still get the right fill function. + (make-local-variable 'normal-auto-fill-function) + (setq normal-auto-fill-function 'matlab-auto-fill) + (make-local-variable 'fill-prefix) + (make-local-variable 'imenu-generic-expression) + (setq imenu-generic-expression matlab-imenu-generic-expression) + ;; Save hook for verifying src. This lets us change the name of + ;; the function in `write-file' and have the change be saved. + ;; It also lets us fix mistakes before a `save-and-go'. + (make-local-variable 'write-contents-hooks) + (add-hook 'write-contents-hooks 'matlab-mode-verify-fix-file-fn) + ;; Tempo tags + (make-local-variable 'tempo-local-tags) + (setq tempo-local-tags (append matlab-tempo-tags tempo-local-tags)) + ;; give each file it's own parameter history + (make-local-variable 'matlab-shell-save-and-go-history) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '((matlab-font-lock-keywords + matlab-gaudy-font-lock-keywords + matlab-really-gaudy-font-lock-keywords + ) + t ; do not do string/comment highlighting + nil ; keywords are case sensitive. + ;; This puts _ as a word constituent, + ;; simplifying our keywords significantly + ((?_ . "w")))) + (matlab-enable-block-highlighting 1) + (if window-system (matlab-frame-init)) + ; If the buffer already has a function definition, figure out the correct + ; settings for matlab-functions-have-end and matlab-indent-function. + (goto-char (point-max)) + (when (eq matlab-indent-function-body 'guess) + ;; Note: Compile warning below, but defined later in this file. + (if (re-search-backward matlab-defun-regex nil t) + (let ((beg (point)) + end ; filled in later + (cc (current-column)) + (nf (let ((matlab-functions-have-end t)) + (condition-case nil + (progn (matlab-forward-sexp) t) + (error nil))))) + (if nf (matlab-toggle-functions-have-end-minor-mode)) ;; observation trumps default + (setq end (if nf (progn (forward-line 0) (point)) (point-max))) + (goto-char beg) + (catch 'done + (while (progn (forward-line 1) (< (point) end)) + (if (looking-at "\\s-*\\(%\\|$\\)") + nil ; go on to next line + (looking-at "\\s-*") + (goto-char (match-end 0)) + (setq matlab-indent-function-body (> (current-column) cc)) + (throw 'done nil))))) + (if (and (bobp) ; the buffer is empty + matlab-functions-have-end) ; user wants this by default + (matlab-toggle-functions-have-end)))) + (if (or (featurep 'mlint) + matlab-show-mlint-warnings + matlab-highlight-cross-function-variables) + ;; Some users may not feel like getting all the extra stuff + ;; needed for mlint working. Do this only if we can get + ;; mlint loaded ok. + (condition-case nil + (mlint-minor-mode + (if (or matlab-show-mlint-warnings matlab-highlight-cross-function-variables) + 1 + 0)) + ;; If there is an error loading the stuff, don't + ;; continue. + (error nil))) + (goto-char (point-min)) + (run-hooks 'matlab-mode-hook) + (if matlab-vers-on-startup (matlab-show-version))) + +;;; Utilities ================================================================= + +(defun matlab-show-version () + "Show the version number in the minibuffer." + (interactive) + (message "matlab-mode, version %s" matlab-mode-version)) + +(defun matlab-find-prev-line () + "Recurse backwards until a code line is found." + (if (= -1 (forward-line -1)) nil + (if (or (matlab-ltype-empty) + (matlab-ltype-comm-ignore)) + (matlab-find-prev-line) t))) + +(defun matlab-prev-line () + "Go to the previous line of code. Return nil if not found." + (interactive) + (let ((old-point (point))) + (if (matlab-find-prev-line) t (goto-char old-point) nil))) + +(defun matlab-uniquafy-list (lst) + "Return a list that is a subset of LST where all elements are unique." + (let ((nlst nil)) + (while lst + (if (and (car lst) (not (member (car lst) nlst))) + (setq nlst (cons (car lst) nlst))) + (setq lst (cdr lst))) + (nreverse nlst))) + +; Aki Vehtari <Aki.Vehtari@hut.fi> recommends this: (19.29 required) +;(require 'backquote) +;(defmacro matlab-navigation-syntax (&rest body) +; "Evaluate BODY with the matlab-mode-special-syntax-table" +; '(let ((oldsyntax (syntax-table))) +; (unwind-protect +; (progn +; (set-syntax-table matlab-mode-special-syntax-table) +; ,@body) +; (set-syntax-table oldsyntax)))) + +(defmacro matlab-navigation-syntax (&rest forms) + "Set the current environment for syntax-navigation and execute FORMS." + (list 'let '((oldsyntax (syntax-table)) + (case-fold-search nil)) + (list 'unwind-protect + (list 'progn + '(set-syntax-table matlab-mode-special-syntax-table) + (cons 'progn forms)) + '(set-syntax-table oldsyntax)))) + +(put 'matlab-navigation-syntax 'lisp-indent-function 0) +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec matlab-navigation-syntax def-body))) + +(defun matlab-up-list (count &optional restrict) + "Move forwards or backwards up a list by COUNT. +Optional argument RESTRICT is where to restrict the search." + ;; MATLAB syntax table has no disabling strings or comments. + (let ((dir (if (> 0 count) -1 +1)) + (origin (point)) + (ms nil)) + ;; Make count positive + (setq count (* count dir)) + (if (= dir -1) + (while (/= count 0) + ;; Search till we find an unstrung paren object. + (setq ms (re-search-backward "\\s(\\|\\s)" restrict t)) + (while (and (save-match-data (matlab-cursor-in-string-or-comment)) + (setq ms (re-search-backward "\\s(\\|\\s)" restrict t)))) + (if (not ms) + (progn + (goto-char origin) + (error "Scan Error: List missmatch"))) + ;; View it's match. + (let ((s (match-string 0))) + (if (string-match "\\s(" s) + (setq count (1- count)) + (setq count (1+ count))))) + (error "Not implemented")) + ms)) + +(defun matlab-valid-end-construct-p () + "Return non-nil if the end after point terminates a block. +Return nil if it is being used to dereference an array." + (let ((p (point)) + (err1 t)) + (condition-case nil + (save-restriction + ;; Restrict navigation only to the current command line + (save-excursion + (matlab-beginning-of-command) + (narrow-to-region (point) + (save-excursion + (goto-char p) + (matlab-point-at-eol)))) + ;; This used to add some sort of protection, but I don't know what + ;; the condition was, or why the simple case doesn't handle it. + ;; + ;; The above replacement fixes a case where a continuation in an array + ;; befuddles the indenter. + ;; (progn ;;(matlab-end-of-command (point)) + ;; (end-of-line) + ;; (if (> p (point)) + ;; (progn + ;; (setq err1 nil) + ;; (error))) + ;; (point)))) + (save-excursion + ;; beginning of param list + (matlab-up-list -1) + ;; backup over the parens. If that fails + (condition-case nil + (progn + (forward-sexp 1) + ;; If we get here, the END is inside parens, which is not a + ;; valid location for the END keyword. As such it is being + ;; used to dereference array parameters + nil) + ;; This error means that we have an unterminated paren + ;; block, so this end is currently invalid. + (error nil)))) + ;; an error means the list navigation failed, which also means we are + ;; at the top-level + (error err1)))) + +;;; Regexps for MATLAB language =============================================== + +;; "-pre" means "partial regular expression" +;; "-if" and "-no-if" means "[no] Indent Function" + +(defconst matlab-defun-regex "^\\(\\s-*function\\|classdef\\)[ \t.[]" + "Regular expression defining the beginning of a MATLAB function.") + +(defconst matlab-mcos-regexp "\\|classdef\\|properties\\|methods\\|enumeration" + "Keywords which mark the beginning of mcos blocks.") + +(defcustom matlab-block-indent-tic-toc-flag nil + "*Non-nil means that tic,toc should indent like a if,end block. +This variable should be set before loading matlab.el" + :group 'matlab + :type 'boolean) + +(defconst matlab-block-beg-pre-if + (if matlab-block-indent-tic-toc-flag + (concat "function\\|parfor\\|spmd\\|for\\|while\\|if\\|switch\\|try\\|tic" + matlab-mcos-regexp) + (concat "function\\|parfor\\|spmd\\|for\\|while\\|if\\|switch\\|try" + matlab-mcos-regexp)) + "Keywords which mark the beginning of an indented block. +Includes function.") + +(defconst matlab-block-beg-pre-no-if + (if matlab-block-indent-tic-toc-flag + (concat "parfor\\|for\\|spmd\\|while\\|if\\|switch\\|try\\|tic" + matlab-mcos-regexp) + (concat "parfor\\|for\\|spmd\\|while\\|if\\|switch\\|try" + matlab-mcos-regexp)) + "Keywords which mark the beginning of an indented block. +Excludes function.") + +(defun matlab-block-beg-pre () + "Partial regular expression to recognize MATLAB block-begin keywords." + (if matlab-functions-have-end + matlab-block-beg-pre-if + matlab-block-beg-pre-no-if)) + +(defconst matlab-block-mid-pre + "elseif\\|else\\|catch" + "Partial regular expression to recognize MATLAB mid-block keywords.") + +(defconst matlab-block-end-pre-if + (if matlab-block-indent-tic-toc-flag + "end\\(function\\)?\\|function\\|\\(\\sw+\\s-*\\((.*)\\)?\\s-*=\\s-*\\)?toc" + "end\\(function\\)?\\|function") + "Partial regular expression to recognize MATLAB block-end keywords.") + +(defconst matlab-block-end-pre-no-if + (if matlab-block-indent-tic-toc-flag + "end\\|\\(\\sw+\\s-*\\((.*)\\)?\\s-*=\\s-*\\)?toc" + "end") + "Partial regular expression to recognize MATLAB block-end keywords.") + +(defun matlab-block-end-pre () + "Partial regular expression to recognize MATLAB block-end keywords." + (if matlab-functions-have-end + matlab-block-end-pre-if + matlab-block-end-pre-no-if)) + +;; Not used. +;;(defconst matlab-other-pre +;; "function\\|return" +;; "Partial regular express to recognize MATLAB non-block keywords.") + +(defconst matlab-endless-blocks + "case\\|otherwise" + "Keywords which initialize new blocks, but don't have explicit ends. +Thus, they are endless. A new case or otherwise will end a previous +endless block, and and end will end this block, plus any outside normal +blocks.") + +(defun matlab-block-re () + "Regular expression for keywords which begin MATLAB blocks." + (concat "\\(^\\|[;,]\\)\\s-*\\(" + (matlab-block-beg-pre) "\\|" + matlab-block-mid-pre "\\|" + (matlab-block-end-pre) "\\|" + matlab-endless-blocks "\\)\\b")) + +(defun matlab-block-scan-re () + "Expression used to scan over matching pairs of begin/ends." + (concat "\\(^\\|[;,]\\)\\s-*\\(" + (matlab-block-beg-pre) "\\|" + (matlab-block-end-pre) "\\)\\b")) + +(defun matlab-block-beg-re () + "Expression used to find the beginning of a block." + (concat "\\(" (matlab-block-beg-pre) "\\)")) + +(defun matlab-block-mid-re () + "Expression used to find block center parts (like else)." + (concat "\\(" matlab-block-mid-pre "\\)")) + +(defun matlab-block-end-re () + "Expression used to end a block. Usually just `end'." + (concat "\\(" (matlab-block-end-pre) "\\)")) + +(defun matlab-block-end-no-function-re () + "Expression representing and end if functions are excluded." + (concat "\\<\\(" matlab-block-end-pre-no-if "\\)\\>")) + +(defun matlab-endless-blocks-re () + "Expression of block starters that do not have associated ends." + (concat "\\(" matlab-endless-blocks "\\)")) + +(defun matlab-match-function-re () + "Expression to match a function start line. +There are no reliable numeric matches in this expression. +Know that `match-end' of 0 is the end of the functin name." + ;; old function was too unstable. + ;;"\\(^function\\s-+\\)\\([^=\n]+=[ \t\n.]*\\)?\\(\\sw+\\)" + (concat "\\(^\\s-*function\\b[ \t\n.]*\\)\\(\\(\\[[^]]*\\]\\|\\sw+\\)" + "[ \t\n.]*=[ \t\n.]*\\|\\(\\)\\)\\(\\sw+\\)")) + +(defconst matlab-cline-start-skip "[ \t]*%[ \t]*" + "*The regular expression for skipping comment start.") + +;;; Lists for matlab keywords ================================================= + +(defvar matlab-keywords-solo + '("break" "case" "else" "elseif" "end" "for" "parfor" "function" "if" "tic" "toc" + "otherwise" "profile" "switch" "while" "try" "catch" "spmd") + "Keywords that appear on a line by themselves.") +(defvar matlab-keywords-return + '("acos" "acosh" "acot" "acoth" "acsch" "asech" "asin" "asinh" + "atan" "atan2" "atanh" "cos" "cosh" "coth" "csc" "csch" "exp" + "log" "log10" "log2" "sec" "sech" "sin" "sinh" "tanh" + "abs" "sign" "sqrt" ) + "List of MATLAB keywords that have return arguments. +This list still needs lots of help.") +(defvar matlab-keywords-boolean + '("all" "any" "exist" "isempty" "isequal" "ishold" "isfinite" "isglobal" + "isinf" "isletter" "islogical" "isnan" "isprime" "isreal" "isspace" + "logical" "isa") + "List of keywords that are typically used as boolean expressions.") + +(defvar matlab-core-properties + '("ButtonDownFcn" "Children" "Clipping" "CreateFcn" "DeleteFcn" + "BusyAction" "HandleVisibility" "HitTest" "Interruptible" + "Parent" "Selected" "SelectionHighlight" "Tag" "Type" + "UIContextMenu" "UserData" "Visible") + "List of properties belonging to all HG objects.") + +(defvar matlab-property-lists + '(("root" . + ("CallbackObject" "Language" "CurrentFigure" "Diary" "DiaryFile" + "Echo" "ErrorMessage" "Format" "FormatSpacing" "PointerLocation" + "MonitorPositions" + "PointerWindow" "Profile" "ProfileFile" "ProfileCount" + "ProfileInterval" "RecursionLimit" "ScreenDepth" "ScreenSize" + "ShowHiddenHandles" "TerminalHideGraphCommand" "TerminalOneWindow" + "TerminalDimensions" "TerminalProtocol" "TerminalShowGraphCommand" + "Units" "AutomaticFileUpdates" )) + ("axes" . + ("AmbientLightColor" "Box" "CameraPosition" "CameraPositionMode" + "CameraTarget" "CameraTargetMode" "CameraUpVector" + "CameraUpVectorMode" "CameraViewAngle" "CameraViewAngleMode" "CLim" + "CLimMode" "Color" "CurrentPoint" "ColorOrder" "DataAspectRatio" + "DataAspectRatioMode" "DrawMode" "FontAngle" "FontName" "FontSize" + "FontUnits" "FontWeight" "GridLineStyle" "Layer" "LineStyleOrder" + "LineWidth" "NextPlot" "PlotBoxAspectRatio" "PlotBoxAspectRatioMode" + "Projection" "Position" "TickLength" "TickDir" "TickDirMode" "Title" + "Units" "View" "XColor" "XDir" "XGrid" "XLabel" "XAxisLocation" "XLim" + "XLimMode" "XScale" "XTick" "XTickLabel" "XTickLabelMode" "XTickMode" + "YColor" "YDir" "YGrid" "YLabel" "YAxisLocation" "YLim" "YLimMode" + "YScale" "YTick" "YTickLabel" "YTickLabelMode" "YTickMode" "ZColor" + "ZDir" "ZGrid" "ZLabel" "ZLim" "ZLimMode" "ZScale" "ZTick" + "ZTickLabel" "ZTickLabelMode" "ZTickMode")) + ("figure" . + ("BackingStore" "CloseRequestFcn" "Color" "Colormap" + "CurrentAxes" "CurrentCharacter" "CurrentObject" "CurrentPoint" + "Dithermap" "DithermapMode" "FixedColors" "IntegerHandle" + "InvertHardcopy" "KeyPressFcn" "MenuBar" "MinColormap" "Name" + "NextPlot" "NumberTitle" "PaperUnits" "PaperOrientation" + "PaperPosition" "PaperPositionMode" "PaperSize" "PaperType" + "Pointer" "PointerShapeCData" "PointerShapeHotSpot" "Position" + "Renderer" "RendererMode" "Resize" "ResizeFcn" "SelectionType" + "ShareColors" "Units" "WindowButtonDownFcn" + "WindowButtonMotionFcn" "WindowButtonUpFcn" "WindowStyle")) + ("image" . ("CData" "CDataMapping" "EraseMode" "XData" "YData")) + ("light" . ("Position" "Color" "Style")) + ("line" . + ("Color" "EraseMode" "LineStyle" "LineWidth" "Marker" "LineSmoothing" + "MarkerSize" "MarkerEdgeColor" "MarkerFaceColor" "XData" "YData" + "ZData")) + ("patch" . + ("CData" "CDataMapping" "FaceVertexCData" "EdgeColor" "EraseMode" + "FaceColor" "Faces" "LineStyle" "LineWidth" "Marker" "LineSmoothing" + "MarkerEdgeColor" "MarkerFaceColor" "MarkerSize" "Vertices" + "XData" "YData" "ZData" "FaceLighting" "EdgeLighting" + "BackFaceLighting" "AmbientStrength" "DiffuseStrength" + "SpecularStrength" "SpecularExponent" "SpecularColorReflectance" + "VertexNormals" "NormalMode")) + ("surface" . + ("CData" "CDataMapping" "EdgeColor" "EraseMode" "FaceColor" + "LineStyle" "LineWidth" "Marker" "MarkerEdgeColor" "LineSmoothing" + "MarkerFaceColor" "MarkerSize" "MeshStyle" "XData" "YData" + "ZData" "FaceLighting" "EdgeLighting" "BackFaceLighting" + "AmbientStrength" "DiffuseStrength" "SpecularStrength" + "SpecularExponent" "SpecularColorReflectance" "VertexNormals" + "NormalMode")) + ("text\\|title\\|xlabel\\|ylabel\\|zlabel" . + ("Color" "EraseMode" "Editing" "Extent" "FontAngle" "FontName" + "FontSize" "FontUnits" "FontWeight" "HorizontalAlignment" + "BackgroundColor" "EdgeColor" "Margin" + "Position" "Rotation" "String" "Units" "Interpreter" + "VerticalAlignment")) + ("uicontextmenu" . ("Callback")) + ("uicontrol" . + ("BackgroundColor" "Callback" "CData" "Enable" "Extent" + "FontAngle" "FontName" "FontSize" "FontUnits" "FontWeight" + "ForegroundColor" "HorizontalAlignment" "ListboxTop" "Max" "Min" + "Position" "String" "Style" "SliderStep" "TooltipString" "Units" + "Value")) + ("uimenu" . + ("Accelerator" "Callback" "Checked" "Enable" "ForegroundColor" + "Label" "Position" "Separator")) + ;; Flesh this out more later. + ("uipushtool\\|uitoggletool\\|uitoolbar" . + ("Cdata" "Callback" "Separator" "Visible")) + ) + "List of property lists on a per object type basis.") + +(defvar matlab-unknown-type-commands + "[gs]et\\|findobj\\|waitfor" + "Expression for commands that have unknown types.") + +(defun matlab-all-known-properties () + "Return a list of all properties." + (let ((lst matlab-core-properties) + (tl matlab-property-lists)) + (while tl + (setq lst (append lst (cdr (car tl))) + tl (cdr tl))) + (matlab-uniquafy-list lst))) + +(defvar matlab-all-known-properties (matlab-all-known-properties) + "List of all the known properties.") + +(defmacro matlab-property-function () + "Regexp of all builtin functions that take property lists." + '(let ((r matlab-unknown-type-commands) + (tl matlab-property-lists)) + (while tl + (setq r (concat r "\\|" (car (car tl))) + tl (cdr tl))) + r)) + +;;; Navigation =============================================================== + +(defvar matlab-scan-on-screen-only nil + "When this is set to non-nil, then forward/backward sexp stops off screen. +This is so the block highlighter doesn't gobble up lots of time when +a block is not terminated.") + +(defun matlab-backward-sexp (&optional autoend noerror) + "Go backwards one balanced set of MATLAB expressions. +If optional AUTOEND, then pretend we are at an end. +If optional NOERROR, then we return t on success, and nil on failure. +This assumes that expressions do not cross \"function\" at the left margin." + (interactive "P") + (matlab-navigation-syntax + (if (and (not autoend) + (save-excursion (backward-word 1) + (or (not + (and (looking-at + (matlab-block-end-no-function-re)) + (matlab-valid-end-construct-p))) + (matlab-cursor-in-string-or-comment)))) + ;; Go backwards one simple expression + (forward-sexp -1) + ;; otherwise go backwards recursively across balanced expressions + ;; backup over our end + (if (not autoend) (forward-word -1)) + (let ((done nil) (start (point)) (returnme t) (bound nil)) + (when (search-backward "\nfunction" nil t) + (if (progn (forward-char 9) (looking-at "\\b")) + (setq bound (- (point) 8))) + (goto-char start)) + (while (and (not done) + (or (not matlab-scan-on-screen-only) + (pos-visible-in-window-p))) + (if (re-search-backward (matlab-block-scan-re) bound t) + (progn + (goto-char (match-beginning 2)) + (if (looking-at (matlab-block-end-no-function-re)) + (if (or (matlab-cursor-in-string-or-comment) + (not (matlab-valid-end-construct-p))) + nil + ;; we must skip the expression and keep searching + (forward-word 1) + (matlab-backward-sexp)) + (if (not (matlab-cursor-in-string-or-comment)) + (setq done t)))) + (goto-char start) + (if noerror + (setq returnme nil) + (error "Unstarted END construct")))) + returnme)))) + +(defun matlab-forward-sexp (&optional includeelse) + "Go forward one balanced set of MATLAB expressions. +Optional argument INCLUDEELSE will stop on ELSE if it matches the starting IF." + (interactive "P") + (let (p) ;; go to here if no error. + (save-excursion ;; don't go anywhere if there is an error + (matlab-navigation-syntax + ;; skip over preceeding whitespace + (skip-chars-forward " \t\n;") + (if (or (not (looking-at (concat "\\(" + (matlab-block-beg-pre) + "\\|" + (matlab-block-mid-re) + "\\)\\>"))) + (matlab-cursor-in-string-or-comment)) + ;; Go forwards one simple expression + (forward-sexp 1) + ;; otherwise go forwards recursively across balanced expressions + (forward-word 1) + (let ((done nil) (s nil) + (expr-scan (if includeelse + (matlab-block-re) + (matlab-block-scan-re))) + (expr-look (matlab-block-beg-pre))) + (while (and (not done) + (setq s (re-search-forward expr-scan nil t)) + (or (not matlab-scan-on-screen-only) + (pos-visible-in-window-p))) + (goto-char (match-beginning 2)) + (if (looking-at expr-look) + (if (matlab-cursor-in-string-or-comment) + (forward-word 1) + ;; we must skip the expression and keep searching + ;; NEVER EVER call with value of INCLUDEELSE + (matlab-forward-sexp)) + (forward-word 1) + (if (and (not (matlab-cursor-in-string-or-comment)) + (matlab-valid-end-construct-p)) + (setq done t)))) + (if (not s) + (error "Unterminated block")))) + (setq p (point)))) ;; really go here + (goto-char p))) + +(defun matlab-indent-sexp () + "Indent the syntactic block starting at point." + (interactive) + (indent-region (point) (save-excursion (matlab-forward-sexp) (point)) nil)) + +(defun matlab-beginning-of-enclosing-defun () + "Move cursor to beginning of enclosing function. +If `matlab-functions-have-end', skip over functions with end." + (catch 'done + (let ((start (point)) + (beg nil)) + (while (re-search-backward matlab-defun-regex nil t) + (setq beg (point)) + (condition-case nil + (progn + (matlab-forward-sexp) + (if (> (point) start) (throw 'done beg))) + (error (throw 'done beg))) + (goto-char beg))) + nil)) + +(defun matlab-beginning-of-defun () + "Go to the beginning of the current function." + (interactive) + (if matlab-functions-have-end + (goto-char (or (matlab-beginning-of-enclosing-defun) (point-min))) + (or (re-search-backward matlab-defun-regex nil t) + (goto-char (point-min))))) + +(defun matlab-end-of-defun () + "Go to the end of the current function." + (interactive) + (or (progn + (if (looking-at matlab-defun-regex) (goto-char (match-end 0))) + (if (re-search-forward matlab-defun-regex nil t) + (progn (forward-line -1) + t))) + (goto-char (point-max)))) + +(defun matlab-current-defun () + "Return the name of the current function." + (save-excursion + (matlab-beginning-of-defun) + (if (looking-at (matlab-match-function-re)) + (progn + (goto-char (match-end 0)) + (current-word))))) + +(defun matlab-beginning-of-command () + "Go to the beginning of an M command. +Travels across continuations." + (interactive) + (beginning-of-line) + (let ((p nil) + ;; This restriction is a wild guess where to end reverse + ;; searching for array continuations. The reason is that + ;; matlab up list is very slow, and most people would never + ;; put a blank line in a matrix. Either way, it's worth the + ;; trade off to speed this up for large files. + ;; This list of keywords is NOT meant to be comprehensive. + (r (save-excursion + (re-search-backward + "^\\s-*\\(%\\|if\\|else\\(if\\)\\|while\\|\\(par\\)?for\\|$\\)\\>" + nil t)))) + (while (and (or (save-excursion (and (matlab-prev-line) + (matlab-lattr-cont))) + (matlab-ltype-continued-comm) + (setq p (matlab-lattr-array-cont r))) + (save-excursion (beginning-of-line) (not (bobp)))) + (if p (goto-char p) (matlab-prev-line)) + (setq p nil)) + (back-to-indentation))) + +(defun matlab-end-of-command (&optional beginning) + "Go to the end of an M command. +Optional BEGINNING is where the command starts from." + (interactive) + (while (and (or (matlab-lattr-cont) + (save-excursion + (forward-line 1) + (or (matlab-ltype-continued-comm) + (matlab-lattr-array-cont beginning)))) + ;; This hack is a short circuit. If a user did not + ;; correctly end a matrix, this will short-circuit + ;; as soon as somethng that would never appear in a matrix + ;; becomes visible. + (not (save-excursion + (beginning-of-line) + (looking-at (matlab-block-scan-re)))) + ;; If we hit the end of the buffer unexpectedly, this test + ;; will fail and we'll avoid looping forever. (E.g., this + ;; is triggered if a continuation line is the last one in + ;; the buffer, and the line lacks the final newline.) + (zerop (forward-line 1)))) + (end-of-line)) + + +;;; Line types and attributes ================================================= + +(defun matlab-ltype-empty () ; blank line + "Return t if current line is empty." + (save-excursion + (beginning-of-line) + (looking-at "^[ \t]*$"))) + +(defun matlab-ltype-comm () ; comment line + "Return t if current line is a MATLAB comment line. +Return the symbol 'cellstart if it is a double %%." + (save-excursion + (beginning-of-line) + (cond ((looking-at "[ \t]*%\\([^%]\\|$\\)") + t) + ((looking-at "[ \t]*%%") + 'cellstart) + (t nil)))) + +(defun matlab-ltype-comm-ignore () ; comment out a region line + "Return t if current line is a MATLAB comment region line." + (save-excursion + (beginning-of-line) + (looking-at (concat "[ \t]*" matlab-comment-region-s)))) + +(defun matlab-ltype-help-comm () + "Return t if the current line is part of the MATLAB help comment." + (save-excursion + (if (not (matlab-ltype-comm)) + nil + (while (and (matlab-ltype-comm) (not (bobp)) + (matlab-prev-line)) + (beginning-of-line)) + (matlab-ltype-function-definition)))) + +(defun matlab-ltype-endfunction-comm () + "Return t if the current line is an ENDFUNCTION style comment." + (save-excursion + (if (not (matlab-ltype-comm)) + nil + (beginning-of-line) + (if (looking-at "^[ \t]*%[ \t]*endfunction") + t + (while (and (or (matlab-ltype-comm) + (matlab-ltype-empty)) + (not (eobp))) + (forward-line 1)) + (matlab-ltype-function-definition))))) + +(defun matlab-ltype-continued-comm () + "Return column of previous line's comment start, or nil." + (save-excursion + (beginning-of-line) + (let ((commtype (matlab-ltype-comm))) + (if (or (eq commtype 'cellstart) ;; Cells are not continuations from previous comments. + (null commtype) + (bobp)) + nil + ;; We use forward-line and not matlab-prev-line because + ;; we want blank lines to terminate this indentation method. + (forward-line -1) + (let ((col (matlab-lattr-comm))) + (if col + (progn + (goto-char col) + (current-column)) + nil)))))) + +(defun matlab-ltype-function-definition () + "Return t if the current line is a function definition." + (save-excursion + (beginning-of-line) + (looking-at matlab-defun-regex))) + +(defun matlab-ltype-code () ; line of code + "Return t if current line is a MATLAB code line." + (and (not (matlab-ltype-empty)) (not (matlab-ltype-comm)))) + +(defun matlab-lattr-comm () ; line has comment + "Return t if current line contain a comment." + (save-excursion (matlab-comment-on-line))) + +(defun matlab-lattr-implied-continuation () + "Return non-nil if this line has implied continuation on the next. +This is only useful for new versions of MATLAB where ... is optional." + (when (not (matlab-lattr-comm)) + (let ((imp nil)) + (save-excursion + (end-of-line) + (skip-chars-backward " \t") + ;; Test for oporator incompleteness. + (setq imp + (/= (point) + ;; Careful, - means range in this expression. + (progn (skip-chars-backward "-+=/*.^&~<>") + (point)))) + (if (not imp) + ;; Test for argument list incompleteness + (condition-case nil + (progn + (end-of-line) + (matlab-up-list -1) + (setq imp (looking-at "("))) + (error nil))) + ) + imp))) + +(defun matlab-lattr-cont () ; line has continuation + "Return non-nil if current line ends in ... and optional comment. +If `matlab-cont-requires-ellipsis' is nil, then we need to apply +a heuristic to determine if this line would use continuation +based on what it ends with." + (save-excursion + (beginning-of-line) + (or + ;; Here, if the line ends in ..., then it is what we are supposed to do. + (and (re-search-forward "[^ \t.][ \t]*\\.\\.+[ \t]*\\(%.*\\)?$" + (matlab-point-at-eol) t) + (progn (goto-char (match-beginning 0)) + (not (matlab-cursor-in-comment)))) + ;; If the line doesn't end in ..., but we have optional ..., then + ;; use this annoying heuristic. + (and (null matlab-cont-requires-ellipsis) + (matlab-lattr-implied-continuation)) + ))) + +(defun matlab-lattr-array-cont (&optional restrict) + "Return non-nil if current line is in an array. +If the entirety of the array is on this line, return nil. +Optional option RESTRICT is the distrance to restrict the search." + (condition-case nil + (save-excursion + (beginning-of-line) + (matlab-up-list -1 restrict) + (and (looking-at "[[{]") (point))) + (error nil))) + +(defun matlab-lattr-array-end () + "Return non-nil if the current line closes an array. +by close, the first character is the end of an array." + (save-excursion + (back-to-indentation) + (and (looking-at "[]}]") (matlab-lattr-array-cont)))) + +(defun matlab-lattr-block-cont (&optional eol) + "Return a number representing the number of unterminated block constructs. +This is any block, such as if or for, that doesn't have an END on this line. +Optional EOL indicates a virtual end of line." + (let ((v 0)) + (save-excursion + (beginning-of-line) + (save-restriction + (narrow-to-region (point) (or eol (matlab-point-at-eol))) + (matlab-navigation-syntax + (while (re-search-forward (concat "\\<" (matlab-block-beg-re) "\\>") + nil t) + (if (matlab-cursor-in-string-or-comment) + ;; Do nothing + nil + ;; Increment counter, move to end. + (setq v (1+ v)) + (let ((p (point))) + (forward-word -1) + (condition-case nil + (progn + (matlab-forward-sexp) + (setq v (1- v))) + (error (goto-char p)))))) + v))))) + +(defun matlab-lattr-middle-block-cont () + "Return the number of middle block continuations. +This should be 1 or nil, and only true if the line starts with one of these +special items." + (save-excursion + (back-to-indentation) + (if (looking-at (concat (matlab-block-mid-re) "\\>")) + (if (and (re-search-forward (matlab-block-end-pre) + (matlab-point-at-eol) + t) + (matlab-valid-end-construct-p)) + ;; If there is an END, we still need to return non-nil, + ;; but the number value is a net of 0. + 0 + 1) + nil))) + +(defun matlab-lattr-endless-block-cont () + "Return the number of middle block continuations. +This should be 1 or nil, and only true if the line starts with one of these +special items." + (save-excursion + (back-to-indentation) + (if (looking-at (concat (matlab-endless-blocks-re) "\\>")) + 1 + nil))) + +(defun matlab-lattr-block-close (&optional start) + "Return the number of closing block constructs. +Argument START is where to start searching from." + (let ((v 0)) + (save-excursion + (when start (goto-char start)) + (save-restriction + (narrow-to-region (save-excursion + (matlab-beginning-of-command) + (point)) + (matlab-point-at-eol)) + (goto-char (point-max)) + (while (and (re-search-backward (concat "\\<" (matlab-block-end-re) "\\>") + nil t) + (not (matlab-cursor-in-string-or-comment)) + (matlab-valid-end-construct-p)) + (setq v (1+ v)) + (let ((startmove (match-end 0)) + (nomove (point))) + (condition-case nil + (progn + (matlab-backward-sexp t) + (setq v (1- v))) + (error (goto-char nomove))) + )) + ;; If we can't scoot back, do a cheat-test to see if there + ;; is a matching else or elseif. + (goto-char (point-min)) + (back-to-indentation) + (if (looking-at (matlab-block-mid-re)) + (setq v (1- v))) + ;; Return nil, or a number + (if (<= v 0) nil v))))) + +(defun matlab-lattr-local-end () + "Return t if this line begins with an end construct." + (save-excursion + (back-to-indentation) + (and (looking-at (concat "\\<" (matlab-block-end-re) "\\>")) + (matlab-valid-end-construct-p)))) + +(defun matlab-lattr-semantics (&optional prefix) + "Return the semantics of the current position. +Values are nil 'solo, 'value, and 'boolean. Boolean is a subset of +value. nil means there is no semantic content (ie, string or comment.) +If optional PREFIX, then return 'solo if that is the only thing on the +line." + (cond ;((matlab-cursor-in-string-or-comment) + ;nil) + ((or (matlab-ltype-empty) + (and prefix (save-excursion + (beginning-of-line) + (looking-at (concat "\\s-*" prefix "\\s-*$"))))) + 'solo) + ((save-excursion + (matlab-beginning-of-command) + (looking-at "\\s-*\\(if\\|elseif\\|while\\)\\>")) + 'boolean) + ((save-excursion + (matlab-beginning-of-command) + (looking-at (concat "\\s-*\\(" (matlab-property-function) + "\\)\\>"))) + 'property) + (t + 'value))) + +(defun matlab-function-called-at-point () + "Return a string representing the function called nearby point." + (save-excursion + (beginning-of-line) + (cond ((looking-at "\\s-*\\([a-zA-Z]\\w+\\)[^=][^=]") + (match-string 1)) + ((and (re-search-forward "=" (matlab-point-at-eol) t) + (looking-at "\\s-*\\([a-zA-Z]\\w+\\)\\s-*[^=]")) + (match-string 1)) + (t nil)))) + +(defun matlab-cursor-in-string-or-comment () + "Return t if the cursor is in a valid MATLAB comment or string." + ;; comment and string depend on each other. Here is one test + ;; that does both. + (save-restriction + (narrow-to-region (matlab-point-at-bol) (matlab-point-at-eol)) + (let ((p (1+ (point))) + (returnme nil) + (sregex (concat matlab-string-start-regexp "'"))) + (save-excursion + (goto-char (point-min)) + (while (and (re-search-forward + (concat "'\\|%\\|" (regexp-quote matlab-elipsis-string)) + nil t) + (<= (point) p)) + (if (or (= ?% (preceding-char)) + (= ?. (preceding-char))) + ;; Here we are in a comment for the rest of it. + (progn + (goto-char p) + (setq returnme t)) + ;; Here, we could be a string start, or transpose... + (if (or (= (current-column) 1) + (save-excursion (forward-char -2) + (looking-at sregex))) + ;; a valid string start, find the end + (let ((f (re-search-forward matlab-string-end-regexp nil t))) + (if f + (setq returnme (> (point) p)) + (setq returnme t))) + ;; Ooops, a transpose, keep going. + )))) + returnme))) + +(defun matlab-cursor-in-comment () + "Return t if the cursor is in a valid MATLAB comment." + (save-match-data + (save-restriction + (narrow-to-region (matlab-point-at-bol) (matlab-point-at-eol)) + (save-excursion + (let ((prev-match nil)) + (while (and (re-search-backward + (concat "%\\|" (regexp-quote matlab-elipsis-string) "+") + nil t) + (not (matlab-cursor-in-string))) + (setq prev-match (point))) + (if (and prev-match (matlab-cursor-in-string)) + (goto-char prev-match)) + (and (looking-at (concat "%\\|" + (regexp-quote matlab-elipsis-string))) + (not (matlab-cursor-in-string)))))))) + +(defun matlab-cursor-in-string (&optional incomplete) + "Return t if the cursor is in a valid MATLAB string. +If the optional argument INCOMPLETE is non-nil, then return t if we +are in what could be a an incomplete string." + (let ((m (match-data)) + (returnme nil)) + (save-restriction + (narrow-to-region (matlab-point-at-bol) (matlab-point-at-eol)) + (let ((p (1+ (point))) + + (sregex (concat matlab-string-start-regexp "'")) + (instring nil)) + (save-excursion + ;; Comment hunters need strings to not call the comment + ;; identifiers. Thus, this routines must be savvy of comments + ;; without recursing to them. + (goto-char (point-min)) + (while (or (and instring (looking-at "'")) + (and (re-search-forward + (concat "'\\|%\\|" + (regexp-quote matlab-elipsis-string)) + nil t) + (<= (point) p) + ;; Short circuit to fix this. + (progn (setq instring nil) t))) + ;; The next line emulates re-search-foward + (if instring (goto-char (match-end 0))) + (if (or (= ?% (preceding-char)) + (= ?. (preceding-char))) + ;; Here we are in a comment for the rest of it. + ;; thus returnme is a force-false. + (goto-char p) + ;; Here, we could be in a string start, or transpose... + (if (or (= (current-column) 1) + instring + (save-excursion (forward-char -2) + (looking-at sregex))) + ;; a valid string start, find the end + (let ((f (re-search-forward matlab-string-end-regexp nil t))) + (if (and (not f) incomplete) + (setq returnme t) + (setq returnme (> (point) p)) + (setq instring t))) + ;; Ooops, a transpose, keep going. + )))))) + (set-match-data m) + returnme)) + + +(defun matlab-comment-on-line () + "Place the cursor on the beginning of a valid comment on this line. +If there isn't one, then return nil, point otherwise." + (interactive) + (let ((eol (matlab-point-at-eol)) + (p (point)) + (signal-error-on-buffer-boundary nil)) + (beginning-of-line) + (while (and (re-search-forward "%" eol t) + (save-excursion (forward-char -1) (matlab-cursor-in-string t)))) + (if (not (bolp)) (forward-char -1)) + (if (and (looking-at "%") (not (matlab-cursor-in-string t))) + (point) + (goto-char p) + nil))) + +;;; Indent functions ========================================================== + +(defun matlab-indent-line () + "Indent a line in `matlab-mode'." + (interactive) + (let ((i (matlab-calc-indent)) + (c (current-column))) + (save-excursion + (back-to-indentation) + (if (= i (current-column)) + nil + (beginning-of-line) + (delete-horizontal-space) + (indent-to i)) + ;; If line contains a comment, format it. + (if () (if (matlab-lattr-comm) (matlab-comment)))) + (if (<= c i) (move-to-column i)))) + +(defun matlab-calc-indent () + "Return the appropriate indentation for this line as an integer." + (interactive) + ;; The first step is to find the current indentation. + ;; This is defined to be zero if all previous lines are empty. + (let* ((ci (save-excursion (if (not (matlab-prev-line)) + 0 + (matlab-next-line-indentation)))) + (sem (matlab-calculate-indentation ci))) + ;; simplistic + (nth 1 sem))) + +(defconst matlab-functions-have-end-should-be-true + "This end closes a function definition.\nDo you want functions to have ends? " + "Prompt the user about whether to change matlab-functions-have-end") + +(defun matlab-calculate-indentation (current-indentation) + "Calculate out the indentation of the current line. +Return a list of descriptions for this line. Return format is: + '(TYPE DEPTHNUMBER) +where TYPE is one of (comment, code, function, blockstart, blockmid, +blockendless, blockend) DEPTHNUMBER is how many characters to indent +this line. + Argument CURRENT-INDENTATION is what the previous line thinks +this line's indentation should be. See `matlab-next-line-indentation'." + (matlab-navigation-syntax + (matlab-calculate-indentation-1 current-indentation))) + +(defun matlab-calculate-indentation-1 (current-indentation) + "Do the indentation work of `matlab-calculate-indentation'. +Argument CURRENT-INDENTATION is what the previous line recommends for indentation." + (let ((ci current-indentation) + (tmp nil)) + (cond + ;; COMMENTS + ((matlab-ltype-comm) + (cond + ;; HELP COMMENT and COMMENT REGION + ((or (matlab-ltype-help-comm) + (matlab-ltype-comm-ignore)) + (list 'comment-help + (save-excursion + (matlab-beginning-of-defun) + (current-indentation)))) + ;; COMMENT Continued From Previous Line + ((setq tmp (matlab-ltype-continued-comm)) + (list 'comment tmp)) + ;; END FUNCTION COMMENT + ((matlab-ltype-endfunction-comm) + (list 'comment-endfunction 0)) + (t + (list 'comment (+ ci matlab-comment-anti-indent))))) + ;; FUNCTION DEFINITION + ((matlab-ltype-function-definition) + (if matlab-functions-have-end + ;; A function line has intrinsic indentation iff function bodies are + ;; not indented and the function line is nested within another function. + (if (and (not matlab-indent-function-body) + (save-excursion + (beginning-of-line) + (matlab-beginning-of-enclosing-defun))) + (setq ci (+ ci matlab-indent-level)) + ;; If no intrinsic indentation, do not change from ci. + ) + ;; If functions are not nested, functions go to left margin. + (setq ci 0)) + (list 'function ci)) + ;; END keyword + ((matlab-lattr-local-end) + (let ((end-of-function + (let ((matlab-functions-have-end t)) + (save-excursion + (beginning-of-line) + (matlab-backward-sexp t) ;; may throw "unstarted block" error + (matlab-ltype-function-definition))))) + (if end-of-function + (if (or matlab-functions-have-end + (if (yes-or-no-p matlab-functions-have-end-should-be-true) + (setq matlab-functions-have-end t) + (error "Unmatched end"))) + (if matlab-indent-function-body + (setq ci (- ci matlab-indent-level)))) + ;; Next, see if this line starts with an end, and whether the + ;; end is matched, and whether the line is blank up to the match. + ;; If so, return the indentation of the match. + (catch 'indent + (save-excursion + (when (progn (beginning-of-line) + (and (looking-at "[ \t]*end\\b") + (matlab-backward-sexp t t))) + (let ((match (point))) + (beginning-of-line) + (looking-at "[ \t]*") + (when (= match (match-end 0)) + (setq ci (- match (match-beginning 0))) + (throw 'indent nil))))) + ;; End of special case for end and match after "^[ \t]*". + (setq ci (+ ci + (* (1- (matlab-lattr-block-cont (point))) + matlab-indent-level)))))) + (list 'blockend ci)) + ;; ELSE/CATCH keywords + ((matlab-lattr-middle-block-cont) + (let ((m (match-string 1))) + (list 'blockmid + (condition-case nil + (save-excursion + (beginning-of-line) + (matlab-backward-sexp t) + (if (matlab-ltype-function-definition) (error "")) + (current-column)) + (error (error "Unmatched %s" m)))))) + ;; CASE/OTHERWISE keywords + ((matlab-lattr-endless-block-cont) + (list 'blockendless + (condition-case nil + (save-excursion + (beginning-of-line) + (matlab-backward-sexp t) + (if (not (looking-at "switch\\>")) (error "")) + (+ (current-column) + (if (listp matlab-case-level) + (car matlab-case-level) + matlab-case-level))) + (error (error "Unmatched case/otherwise part"))))) + ;; End of a MATRIX + ((matlab-lattr-array-end) + (list 'array-end (save-excursion + (back-to-indentation) + (matlab-up-list -1) + (let* ((fc (following-char)) + (mi (assoc fc matlab-maximum-indents)) + (max (if mi (if (listp (cdr mi)) + (car (cdr mi)) (cdr mi)) + nil)) + (ind (if mi (if (listp (cdr mi)) + (cdr (cdr mi)) (cdr mi)) + nil))) + ;; apply the maximum limits. + (if (and ind (> (- (current-column) ci) max)) + (1- ind) ; decor + (current-column)))))) + ;; Code lines + ((save-excursion + (beginning-of-line) + (back-to-indentation) + (= (point) (progn (matlab-beginning-of-command) (point)))) + ;; This means we are at the beginning of a command structure. + ;; Always match up against the previous line. + (list 'code ci)) + ;; Lines continued from previous statements. + (t + (list (if (matlab-ltype-empty) 'empty + (if (matlab-lattr-array-cont) 'array-cont 'code)) + (condition-case nil + ;; Line up with opening paren/brace/bracket + (let ((boc (save-excursion + (matlab-beginning-of-command) + (point)))) + (save-excursion + (beginning-of-line) + (matlab-up-list -1) + (if (> boc (point)) (error nil)) + ;; Ok, it MIGHT be that we are in a program + ;; statement, and this particular command is an HG + ;; statement that would look better if the + ;; following lines lined up AFTER the first + ;; argument. Lets look. + (let ((parendepth (current-column))) + (cond ((and (= (following-char) ?\( ) + (save-excursion + (matlab-navigation-syntax + (forward-word -1) + (looking-at + matlab-indent-past-arg1-functions))) + (let ((start-paren (point))) + (while + (and + (re-search-forward + "," (matlab-point-at-eol) t) + (save-excursion + (matlab-up-list -1) + (> (point) start-paren)))) + (if (and + (= (preceding-char) ?,) + ;; Don't bother if we hit the EOL. + (not (looking-at + + "\\s-*\\(\\.\\.\\.\\|$\\|)\\)"))) + t + (move-to-column parendepth) + nil))) + (skip-chars-forward " \t") + (if (> (- (current-column) parendepth) + matlab-arg1-max-indent-length) + (+ parendepth matlab-arg1-max-indent-length) + (current-column))) + (t + (let* ((fc (following-char)) + (mi (assoc fc matlab-maximum-indents)) + (max (if mi + (if (listp (cdr mi)) + (car (cdr mi)) (cdr mi)) + nil)) + (ind (if mi + (if (listp (cdr mi)) + (cdr (cdr mi)) (cdr mi)) + nil))) + (forward-char 1) + (skip-chars-forward " \t") + ;; If we are at the end of a line and + ;; this open paren is there, then we + ;; DONT want to indent to it. Use the + ;; standard indent. + (if (looking-at "\\.\\.\\.\\|$") + ;; This could happen in another set + ;; of matricies. Find a current + ;; indentation based on the + ;; previous line. + (let ((cci (current-indentation))) + (+ cci matlab-cont-level)) + ;; apply the maximum limits. + (if (and ind (> (- (current-column) ci) max)) + (+ ci ind) + (current-column))))))))) + (error + ;; Line up to an equals sign. + (save-excursion + (matlab-beginning-of-command) + (while (and (re-search-forward "=" (matlab-point-at-eol) t) + (matlab-cursor-in-string-or-comment))) + (if (/= (preceding-char) ?=) + (+ ci matlab-cont-level) + (skip-chars-forward " \t") + (let ((cc (current-column)) + (mi (assoc ?= matlab-maximum-indents))) + (if (looking-at "\\.\\.\\.\\|$") + ;; In this case, the user obviously wants the + ;; indentation to be somewhere else. + (+ ci (cdr (cdr mi))) + ;; If the indent delta is greater than the max, + ;; use the max + currenti + (if (and mi (> (- cc ci) (if (listp (cdr mi)) + (car (cdr mi)) + (cdr mi)))) + (setq cc (+ ci (if (listp (cdr mi)) + (cdr (cdr mi)) + (cdr mi))))) + cc)))))))) + ))) + +(defun matlab-next-line-indentation () + "Calculate the indentation for lines following this command line. +Assume that the following line does not contribute its own indentation +\(as it does in the case of nested functions in the following situations): + o function---positive indentation when not indenting function bodies. + o end---negative indentation except when the 'end' matches a function and + not indenting function bodies. +See `matlab-calculate-indentation'." + (matlab-navigation-syntax + (let ((startpnt (point-at-eol))) + (save-excursion + (matlab-beginning-of-command) + (let ((cc (or (matlab-lattr-block-close startpnt) 0)) + (end (matlab-lattr-local-end)) + (bc (matlab-lattr-block-cont startpnt)) + (mc (matlab-lattr-middle-block-cont)) + (ec (matlab-lattr-endless-block-cont)) + (hc (and matlab-indent-function-body (matlab-ltype-help-comm))) + (rc (and (/= 0 matlab-comment-anti-indent) + (matlab-ltype-comm) + (not (matlab-ltype-help-comm)) + (not (matlab-ltype-continued-comm)) + (not (matlab-ltype-endfunction-comm)))) + (ci (current-indentation))) + ;; When the current point is on a line with a function, the value of bc will + ;; reflect the function in a block count iff if matlab-functions-have-end is + ;; true. However, if matlab-indent-function-body is false, there should be + ;; no actual indentation, so bc needs to be decremented by 1. Similarly, if + ;; on a line with an end that closes a function, bc needs to be decremented + ;; by 1 if matlab-functions-have-end is true and matlab-indent-function-body + ;; is false. However, just to be safe, indentation is not allowed to go + ;; negative. Thus: + (if matlab-functions-have-end + (if (and + (not matlab-indent-function-body) + (or (matlab-ltype-function-definition) + (and (matlab-lattr-local-end) + (save-excursion + (matlab-backward-sexp t) + (looking-at "function\\b"))))) + (if (> bc 0) + (setq bc (1- bc)) + (if (>= ci matlab-indent-level) + (setq bc -1)))) + (if (and matlab-indent-function-body (matlab-ltype-function-definition)) + (setq bc (1+ bc)))) + ;; Remove 1 from the close count if there is an END on the beginning + ;; of this line, since in that case, the unindent has already happened. + (when end (setq cc (1- cc))) + ;; Calculate the suggested indentation. + (+ ci + (* matlab-indent-level bc) + (* matlab-indent-level (or mc 0)) + (* matlab-indent-level (- cc)) + (* (if (listp matlab-case-level) + (cdr matlab-case-level) matlab-case-level) + (or ec 0)) + (if hc matlab-indent-level 0) + (if rc (- 0 matlab-comment-anti-indent) 0) + )))))) + +;;; The return key ============================================================ + +(defcustom matlab-return-function 'matlab-indent-end-before-ret + "Function to handle return key. +Must be one of: + 'matlab-plain-ret + 'matlab-indent-after-ret + 'matlab-indent-end-before-ret + 'matlab-indent-before-ret" + :group 'matlab + :type '(choice (function-item matlab-plain-ret) + (function-item matlab-indent-after-ret) + (function-item matlab-indent-end-before-ret) + (function-item matlab-indent-before-ret))) + +(defun matlab-return () + "Handle carriage return in `matlab-mode'." + (interactive) + (matlab-semicolon-on-return) + (funcall matlab-return-function)) + +(defun matlab-plain-ret () + "Vanilla new line." + (interactive) + (newline)) + +(defun matlab-indent-after-ret () + "Indent after new line." + (interactive) + (newline) + (matlab-indent-line)) + +(defun matlab-indent-end-before-ret () + "Indent line if block end, start new line, and indent again." + (interactive) + (if (save-excursion + (beginning-of-line) + (looking-at (concat "^\\s-*\\(" (matlab-block-end-re) + "\\|" (matlab-block-mid-re) + "\\|" (matlab-endless-blocks-re) + "\\|function\\)"))) + (matlab-indent-line)) + (newline) + (matlab-indent-line)) + +(defun matlab-semicolon-on-return () + "If needed, add a semicolon at point automatically." + (if matlab-return-add-semicolon + (if (and (not (matlab-ltype-empty)) + (not (save-excursion + (skip-chars-backward " \t;" (matlab-point-at-bol)) + (looking-at "\\s-*;"))) + (save-excursion + (let ((p (point))) + (matlab-end-of-command (point)) + (eq p (point)))) + (save-excursion + (matlab-beginning-of-command) + ;; Note: Compile warning below, but defined later. + (not (looking-at matlab-quiesce-nosemi-regexp)))) + (insert ";")) + )) + +(defun matlab-indent-before-ret () + "Indent line, start new line, and indent again." + (interactive) + (matlab-indent-line) + (newline) + (matlab-indent-line)) + +(defun matlab-linefeed () + "Handle line feed in `matlab-mode'. +Has effect of `matlab-return' with (not matlab-indent-before-return)." + (interactive) + (matlab-indent-line) + (newline) + (matlab-indent-line)) + +(defun matlab-comment-return () + "Handle carriage return for MATLAB comment line." + (interactive) + (cond + ((matlab-ltype-comm) + (matlab-set-comm-fill-prefix) (newline) (insert fill-prefix) + (matlab-reset-fill-prefix) (matlab-indent-line)) + ((matlab-lattr-comm) + (newline) (indent-to comment-column) + (insert matlab-comment-on-line-s)) + (t + (newline) (matlab-comment) (matlab-indent-line)))) + +(defun matlab-comm-from-prev () + "If the previous line is a comment-line then set up a comment on this line." + (save-excursion + ;; If the previous line is a comment-line then set the fill prefix from + ;; the previous line and fill this line. + (if (and (= 0 (forward-line -1)) (matlab-ltype-comm)) + (progn + (matlab-set-comm-fill-prefix) + (forward-line 1) (beginning-of-line) + (delete-horizontal-space) + (if (looking-at "%") (delete-char 1)) + (delete-horizontal-space) + (insert fill-prefix) + (matlab-reset-fill-prefix))))) + +(defun matlab-electric-comment (arg) + "Indent line and insert comment character. +Argument ARG specifies how many %s to insert." + (interactive "P") + (self-insert-command (or arg 1)) + (when (matlab-ltype-comm) + (matlab-indent-line) + ;; The above seems to put the cursor on the %, not after it. + (skip-chars-forward "%"))) + + +;;; Comment management======================================================== + +(defun matlab-comment () + "Add a comment to the current line." + (interactive) + (cond ((matlab-ltype-empty) ; empty line + (matlab-comm-from-prev) + (if (matlab-lattr-comm) + (skip-chars-forward " \t%") + (insert matlab-comment-line-s) + (matlab-indent-line))) + ((matlab-ltype-comm) ; comment line + (matlab-comm-from-prev) + (skip-chars-forward " \t%")) + ((matlab-lattr-comm) ; code line w/ comment + (beginning-of-line) + (re-search-forward "[^%]%[ \t]") + (forward-char -2) + (if (> (current-column) comment-column) (delete-horizontal-space)) + (if (< (current-column) comment-column) (indent-to comment-column)) + (skip-chars-forward "% \t")) + (t ; code line w/o comment + (end-of-line) + (re-search-backward "[^ \t\n^]" 0 t) + (forward-char) + (delete-horizontal-space) + (if (< (current-column) comment-column) + (indent-to comment-column) + (insert " ")) + (insert matlab-comment-on-line-s)))) + +(defun matlab-comment-line-break-function (&optional soft) + "Break the current line, and if in a comment, continue it. +Optional argument SOFT indicates that the newline is soft, and not hard." + (interactive) + (if (not (matlab-cursor-in-comment)) + (matlab-return) + ;; Will the below fn work in old emacsen? + (if soft (insert-and-inherit ?\n) (newline 1)) + (insert "% ") + (matlab-indent-line) + (end-of-line))) + +(defun matlab-comment-indent () + "Indent a comment line in `matlab-mode'." + (matlab-calc-indent)) + +(defun matlab-comment-region (beg-region end-region arg) + "Comments every line in the region. +Puts `matlab-comment-region-s' at the beginning of every line in the region. +BEG-REGION and END-REGION are arguments which specify the region boundaries. +With non-nil ARG, uncomments the region." + (interactive "*r\nP") + (let ((end-region-mark (make-marker)) (save-point (point-marker))) + (set-marker end-region-mark end-region) + (goto-char beg-region) + (beginning-of-line) + (if (not arg) ;comment the region + (progn (insert matlab-comment-region-s) + (while (and (= (forward-line 1) 0) + (< (point) end-region-mark)) + (insert matlab-comment-region-s))) + (let ((com (regexp-quote matlab-comment-region-s))) ;uncomment the region + (if (looking-at com) + (delete-region (point) (match-end 0))) + (while (and (= (forward-line 1) 0) + (< (point) end-region-mark)) + (if (looking-at com) + (delete-region (point) (match-end 0)))))) + (goto-char save-point) + (set-marker end-region-mark nil) + (set-marker save-point nil))) + +(defun matlab-uncomment-region (beg end) + "Uncomment the current region if it is commented out. +Argument BEG and END indicate the region to uncomment." + (interactive "*r") + (matlab-comment-region beg end t)) + +;;; Filling =================================================================== + +(defun matlab-set-comm-fill-prefix () + "Set the `fill-prefix' for the current (comment) line." + (interactive) + (if (matlab-lattr-comm) + (setq fill-prefix + (save-excursion + (beginning-of-line) + (let ((e (matlab-point-at-eol)) + (pf nil)) + (while (and (re-search-forward "%+[ \t]*\\($$$ \\)?" e t) + (matlab-cursor-in-string))) + (setq pf (match-string 0)) + (concat (make-string (- (current-column) (length pf)) ? ) + pf)))))) + +(defun matlab-set-comm-fill-prefix-post-code () + "Set the `fill-prefix' for the current post-code comment line." + (interactive) + (matlab-set-comm-fill-prefix)) + +(defun matlab-reset-fill-prefix () + "Reset the `fill-prefix'." + (setq fill-prefix nil)) + +(defun matlab-find-convenient-line-break () + "For the current line, position the cursor where we want to break the line. +Basically, spaces are best, then operators. Always less than `fill-column' +unless we decide we can fudge the numbers. Return nil if this line should +not be broken. This function will ONLY work on code." + ;; First of all, if this is a continuation, then the user is + ;; requesting that we don't mess with his stuff. + (if (matlab-lattr-cont) + nil + (save-restriction + (narrow-to-region (matlab-point-at-bol) (matlab-point-at-eol)) + ;; get ourselves onto the fill-column. + (move-to-column fill-column) + (let ((pos nil) + (orig (point))) + (or + ;; Next, if we have a trailing comment, use that. + (progn (setq pos (or (matlab-lattr-comm) (matlab-point-at-bol))) + (goto-char pos) + (if (and (> (current-column) (- fill-column matlab-fill-fudge)) + (< (current-column) (+ fill-column matlab-fill-fudge))) + t + (goto-char orig) + nil)) + ;; Now, lets find the nearest space (after or before fill column) + (let* ((after (save-excursion + (re-search-forward "[ \t]" nil t))) + (before (save-excursion + (re-search-backward "[ \t]" nil t))) + (afterd (- (or after (matlab-point-at-eol)) (point))) + (befored (- (point) (or before (matlab-point-at-bol))))) + ;; Here, if "before" is actually the beginning of our + ;; indentation, then this is most obiously a bad place to + ;; break our lines. + (if before + (save-excursion + (goto-char before) + (if (<= (point) (save-excursion + (back-to-indentation) + (point))) + (setq before nil)))) + (cond ((and after + (< afterd matlab-fill-fudge) + (< afterd befored)) + (goto-char after) + t) + ((and before + (< befored matlab-fill-fudge) + (< befored afterd)) + (goto-char before) + t) + (t (goto-char orig) + nil))) + ;; Now, lets find the nearest backwards + (progn + (re-search-backward "\\(\\s-\\|\\s.\\)+" nil t) + (while (and (looking-at "\\^\\|\\.\\|'") + (re-search-backward "\\(\\s-\\|\\s.\\)+" nil t))) + (if (or (not (looking-at "\\(\\s-\\|\\s.\\)+")) + (<= (point) (save-excursion + (back-to-indentation) + (point)))) + (progn + ;; We failed in our mission to find anything, or fell + ;; of the edge of the earth. If we are out of + ;; bounds, lets try again. + (goto-char orig) + (if (re-search-backward "\\s.+" nil t) + t + nil)) + ;; Ok, we have a good location to break. Check for column + ;; and ref against nearest list ending to predict a possibly + ;; better break point. + (forward-char 1) + (let ((okpos (current-column)) + (startlst (save-excursion + (condition-case nil + (matlab-up-list -1) + (error nil)) + (if (save-excursion + (forward-char -1) + (looking-at "\\w")) + (forward-word -1)) + (current-column))) + (endlst (save-excursion + (condition-case nil + (matlab-up-list 1) + (error nil)) + (current-column)))) + ;; When evaluating list fudge factores, breaking on the + ;; edge of a list, or at the beginning of a function + ;; call can be more valuable than breaking on a symbol + ;; of a mid-sized list. As such, allow double-fudge + ;; for lists. + (cond + ;; First, pick the end of a list. + ((and (< endlst matlab-fill-fudge-hard-maximum) + (<= endlst (+ fill-column matlab-fill-fudge)) + (or (<= (* matlab-fill-fudge 2) (- endlst okpos)) + (<= endlst fill-column)) + (save-excursion + (move-to-column endlst) + (not (looking-at "\\^")))) + (move-to-column endlst) + t) + ;; Else, back up over this list and poke around + ((>= (* 2 matlab-fill-fudge) (- okpos startlst)) + (move-to-column startlst) + t) + ;; Oh well, just do this symbol. + (t (move-to-column okpos) + t))))) + ;; Well, this just sucks + (progn (goto-char orig) + nil)))))) + +(defun matlab-auto-fill () + "Do auto filling. +Set variable `auto-fill-function' to this symbol to enable MATLAB style auto +filling which will automatically insert `...' and the end of a line." + (interactive) + (let ((fill-prefix fill-prefix) ;; safe way of modifying fill-prefix. + (fill-column (- fill-column + (if matlab-fill-count-ellipsis-flag + (save-excursion + (move-to-column fill-column) + (if (not (bobp)) + (forward-char -1)) + (if (matlab-cursor-in-string 'incomplete) + 4 3)) + 0)))) + (if (> (current-column) fill-column) + (cond + ((matlab-ltype-comm-ignore) + nil) + ((or (matlab-ltype-comm) + (and (save-excursion (move-to-column fill-column) + (matlab-cursor-in-comment)) + (matlab-lattr-comm))) + ;; If the whole line is a comment, do this. + (matlab-set-comm-fill-prefix) (do-auto-fill) + (matlab-reset-fill-prefix)) + ((and (matlab-ltype-code) + (not (matlab-lattr-cont)) + matlab-fill-code) + ;; If we are on a code line, we ellipsify before we fill. + (let ((m (make-marker))) + (move-marker m (point)) + (set-marker-insertion-type m t) + (if (not (matlab-find-convenient-line-break)) + nil + (if (not (save-excursion + (forward-char -1) + (matlab-cursor-in-string 'incomplete))) + (progn + (delete-horizontal-space) + (insert " " matlab-elipsis-string "\n") + (matlab-indent-line)) + (if matlab-fill-strings-flag + (let ((pos (point)) + (pos2 nil)) + (while (and (re-search-backward "'" (point-at-bol) t) + (progn (forward-char -1) + (looking-at "''")))) + (setq pos2 (point)) + ;; Check if there is already an opening bracket or if string is continued + (if (or (looking-at "\\[") + (save-excursion (skip-chars-backward " \t") + (forward-char -1) + (looking-at "\\[")) + (progn + (beginning-of-line) + (skip-chars-backward (concat " \t\n" matlab-elipsis-string)) + (if (> (point) (point-min)) + (progn + (forward-char -1) + (looking-at (concat "'\\s-*" matlab-elipsis-string)))))) + (goto-char pos) + (goto-char pos2) + (forward-char 1) + (insert "[") + (goto-char pos) + (forward-char 1)) + ;(delete-horizontal-space) + (skip-chars-forward " \t") + (insert "' " matlab-elipsis-string "\n") + (matlab-indent-line) + (insert "'") + ;; Re scan forward for the end of the string. Add an end bracket + ;; if there isn't one already. Also add an apostrophe if necessary. + (if (not (looking-at "'\\s-*]")) + (save-excursion + (if (not (re-search-forward "[^']'[^']" (line-end-position) t)) + (progn + (end-of-line) + (insert "']") + (move-marker m (- (point) 2))) + (forward-char -2) + (if (not (looking-at "'\\s-*]")) + (progn + (forward-char 1) + (insert "]")))))) + )))) + (goto-char m))) + )))) + +(defun matlab-join-comment-lines () + "Join current comment line to the next comment line." + ;; New w/ V2.0: This used to join the previous line, but I could find + ;; no editors that had a "join" that did that. I modified join to have + ;; a behaviour I thought more inline with other editors. + (interactive) + (end-of-line) + (if (looking-at "\n[ \t]*%") + (replace-match " " t t nil) + (error "No following comment to join with"))) + +(defun matlab-fill-region (beg-region end-region &optional justify-flag) + "Fill the region between BEG-REGION and END-REGION. +Non-nil JUSTIFY-FLAG means justify comment lines as well." + (interactive "*r\nP") + (let ((end-reg-mk (make-marker))) + (set-marker end-reg-mk end-region) + (goto-char beg-region) + (beginning-of-line) + (while (< (point) end-reg-mk) + ;; This function must also leave the point at the end of the + ;; justified line. + (matlab-fill-paragraph justify-flag) + (forward-line 1) + (beginning-of-line)))) + +(defun matlab-fill-comment-line (&optional justify) + "Fill the current comment line. +With optional argument, JUSTIFY the comment as well." + (interactive) + (if (not (matlab-comment-on-line)) + (error "No comment to fill")) + (beginning-of-line) + ;; First, find the beginning of this comment... + (while (and (looking-at matlab-cline-start-skip) + (not (bobp))) + (forward-line -1) + (beginning-of-line)) + (if (not (looking-at matlab-cline-start-skip)) + (forward-line 1)) + ;; Now scan to the end of this comment so we have our outer bounds, + ;; and narrow to that region. + (save-restriction + (narrow-to-region (point) + (save-excursion + (while (and (looking-at matlab-cline-start-skip) + (not (save-excursion (end-of-line) (eobp)))) + (forward-line 1) + (beginning-of-line)) + (if (not (looking-at matlab-cline-start-skip)) + (forward-line -1)) + (end-of-line) + (point))) + ;; Find the fill prefix... + (matlab-comment-on-line) + (looking-at "%[ \t]*") + (let ((fill-prefix (concat (make-string (current-column) ? ) + (match-string 0)))) + (fill-region (point-min) (point-max) justify)))) + +(defun matlab-justify-line () + "Delete space on end of line and justify." + (interactive) + (save-excursion + (end-of-line) + (delete-horizontal-space) + (justify-current-line))) + +(defun matlab-fill-paragraph (arg) + "When in a comment, fill the current paragraph. +Paragraphs are always assumed to be in a comment. +ARG is passed to `fill-paragraph' and will justify the text." + (interactive "P") + (cond ((or (matlab-ltype-comm) + (and (matlab-cursor-in-comment) + (not (matlab-lattr-cont)))) + ;; We are in a comment, lets fill the paragraph with some + ;; nice regular expressions. + ;; Cell start/end markers of %% also separate paragraphs + (let ((paragraph-separate "%%\\|%[a-zA-Z]\\|%[ \t]*$\\|[ \t]*$") + (paragraph-start "%[a-zA-Z]\\|%[ \t]*$\\|[ \t]*$") + (paragraph-ignore-fill-prefix nil) + (start (save-excursion (matlab-beginning-of-command) + (if (looking-at "%%") + (progn (end-of-line) + (forward-char 1))) + (point))) + (end (save-excursion (matlab-end-of-command) + (point))) + (fill-prefix nil)) + (matlab-set-comm-fill-prefix) + (save-restriction + ;; Ben North fixed to handle comment at the end of + ;; a buffer. + (narrow-to-region start (min (point-max) (+ end 1))) + (fill-paragraph arg)))) + ((matlab-ltype-code) + ;; Ok, lets get the outer bounds of this command, then + ;; completely refill it using the smart line breaking code. + (save-restriction + (narrow-to-region (save-excursion + (matlab-beginning-of-command) + (beginning-of-line) + (point)) + (save-excursion + (matlab-end-of-command) + (point))) + ;; Remove all line breaks + (goto-char (point-min)) + (while (and (re-search-forward "$" nil t) + (not (eobp))) + (delete-horizontal-space) + ;; Blow away continuation marks + (if (matlab-lattr-cont) + (progn + (goto-char (match-beginning 0)) + (forward-char 1) + (delete-region (point) (matlab-point-at-eol)))) + ;; Zap the CR + (if (not (eobp)) (delete-char 1)) + ;; Clean up whitespace + (delete-horizontal-space) + ;; Clean up trailing comments + (if (and (looking-at "% *") + (matlab-cursor-in-comment)) + (progn + (delete-char 1) + (delete-horizontal-space))) + (insert " ")) + ;; Now fill till we are done + (goto-char (point-max)) + (while (or (> (current-column) (+ fill-column matlab-fill-fudge)) + (> (current-column) matlab-fill-fudge-hard-maximum)) + (if (= (point) + (progn + (matlab-auto-fill) + (point))) + (error "Fill algorith failed!")) + (if arg (save-excursion + (forward-line -1) + (matlab-justify-line)))) + (if arg (save-excursion + (forward-line -1) + (matlab-justify-line))))) + (t + (message "Paragraph Fill not supported in this context.")))) + +;;; Semantic text insertion and management ==================================== + +(defun matlab-find-recent-variable-list (prefix) + "Return a list of most recent variables starting with PREFIX as a string. +Reverse searches for the following are done first: + 1) Assignment + 2) if|for|while|switch <var> + 3) global variables + 4) function arguments. +All elements are saved in a list, which is then uniqafied. +If NEXT is non-nil, then the next element from the saved list is used. +If the list is empty, then searches continue backwards through the code." + (matlab-navigation-syntax + (let* ((bounds (save-excursion + (if (re-search-backward "^\\s-*function\\>" nil t) + (match-beginning 0) (point-min)))) + (syms + (append + (save-excursion + (let ((lst nil)) + (while (and + (re-search-backward + (concat "^\\s-*\\(" prefix "\\w+\\)\\s-*=") + bounds t) + (< (length lst) 10)) + (setq lst (cons (match-string 1) lst))) + (nreverse lst))) + (save-excursion + (let ((lst nil)) + (while (and (re-search-backward + (concat "\\<\\(" matlab-block-beg-pre-no-if + "\\)\\s-+(?\\s-*\\(" prefix + "\\w+\\)\\>") + bounds t) + (< (length lst) 10)) + (setq lst (cons (match-string 2) lst))) + (nreverse lst))) + (save-excursion + (if (re-search-backward "^\\s-*global\\s-+" bounds t) + (let ((lst nil) m e) + (goto-char (match-end 0)) + (while (looking-at "\\(\\w+\\)\\([ \t]+\\|$\\)") + (setq m (match-string 1) + e (match-end 0)) + (if (equal 0 (string-match prefix m)) + (setq lst (cons m lst))) + (goto-char e)) + (nreverse lst)))) + (save-excursion + (if (and (re-search-backward "^\\s-*function\\>" bounds t) + (re-search-forward "\\<\\(\\w+\\)(" + (matlab-point-at-eol) t)) + (let ((lst nil) m e) + (while (looking-at "\\(\\w+\\)\\s-*[,)]\\s-*") + (setq m (match-string 1) + e (match-end 0)) + (if (equal 0 (string-match prefix m)) + (setq lst (cons m lst))) + (goto-char e)) + (nreverse lst)))))) + (fl nil)) + (while syms + (if (car syms) (setq fl (cons (car syms) fl))) + (setq syms (cdr syms))) + (matlab-uniquafy-list (nreverse fl))))) + +(defvar matlab-most-recent-variable-list nil + "Maintained by `matlab-find-recent-variable'.") + +(defun matlab-find-recent-variable (prefix &optional next) + "Return the most recently used variable starting with PREFIX as a string. +See `matlab-find-recent-variable-list' for details. +In NEXT is non-nil, than continue through the list of elements." + (if next + (let ((next (car matlab-most-recent-variable-list))) + (setq matlab-most-recent-variable-list + (cdr matlab-most-recent-variable-list)) + next) + (let ((syms (matlab-find-recent-variable-list prefix)) + (first nil)) + (if (eq matlab-completion-technique 'complete) + syms + (setq first (car syms)) + (setq matlab-most-recent-variable-list (cdr syms)) + first)))) + +(defun matlab-find-user-functions-list (prefix) + "Return a list of user defined functions that match PREFIX." + (matlab-navigation-syntax + (let ((syms + (append + (save-excursion + (goto-char (point-min)) + (let ((lst nil)) + (while (re-search-forward "^\\s-*function\\>" nil t) + (if (re-search-forward + (concat "\\(" prefix "\\w+\\)\\s-*\\($\\|(\\)") + (matlab-point-at-eol) t) + (setq lst (cons (match-string 1) lst)))) + (nreverse lst))) + (let ((lst nil) + (files (directory-files + default-directory nil + (concat "^" prefix + "[a-zA-Z][a-zA-Z0-9_]+\\.m$")))) + (while files + (setq lst (cons (progn (string-match "\\.m" (car files)) + (substring (car files) 0 + (match-beginning 0))) + lst) + files (cdr files))) + lst))) + (fl nil)) + (while syms + (if (car syms) (setq fl (cons (car syms) fl))) + (setq syms (cdr syms))) + (matlab-uniquafy-list (nreverse fl))))) + +(defvar matlab-user-function-list nil + "Maintained by `matlab-find-user-functions'.") + +(defun matlab-find-user-functions (prefix &optional next) + "Return a user function that match PREFIX and return it. +If optional argument NEXT is non-nil, then return the next found +object." + (if next + (let ((next (car matlab-user-function-list))) + (setq matlab-user-function-list (cdr matlab-user-function-list)) + next) + (let ((syms (matlab-find-user-functions-list prefix)) + (first nil)) + (if (eq matlab-completion-technique 'complete) + syms + (setq first (car syms)) + (setq matlab-user-function-list (cdr syms)) + first)))) + +(defvar matlab-generic-list-placeholder nil + "Maintained by `matalb-generic-list-expand'. +Holds sub-lists of symbols left to be expanded.") + +(defun matlab-generic-list-expand (list prefix &optional next) + "Return an element from LIST that start with PREFIX. +If optional NEXT argument is non nil, then the next element in the +list is used. nil is returned if there are not matches." + (if next + (let ((next (car matlab-generic-list-placeholder))) + (setq matlab-generic-list-placeholder + (cdr matlab-generic-list-placeholder)) + next) + (let ((re (concat "^" (regexp-quote prefix))) + (first nil) + (fl nil)) + (while list + (if (string-match re (car list)) + (setq fl (cons (car list) fl))) + (setq list (cdr list))) + (setq fl (nreverse fl)) + (if (eq matlab-completion-technique 'complete) + fl + (setq first (car fl)) + (setq matlab-generic-list-placeholder (cdr fl)) + first)))) + +(defun matlab-solo-completions (prefix &optional next) + "Return PREFIX matching elements for solo symbols. +If NEXT then the next patch from the list is used." + (matlab-generic-list-expand matlab-keywords-solo prefix next)) + +(defun matlab-value-completions (prefix &optional next) + "Return PREFIX matching elements for value symbols. +If NEXT then the next patch from the list is used." + (matlab-generic-list-expand matlab-keywords-return prefix next)) + +(defun matlab-boolean-completions (prefix &optional next) + "Return PREFIX matching elements for boolean symbols. +If NEXT then the next patch from the list is used." + (matlab-generic-list-expand matlab-keywords-boolean prefix next)) + +(defun matlab-property-completions (prefix &optional next) + "Return PREFIX matching elements for property names in strings. +If NEXT then the next property from the list is used." + (let ((f (matlab-function-called-at-point)) + (lst matlab-property-lists) + (foundlst nil) + (expandto nil)) + ;; Look for this function. If it is a known function then we + ;; can now use a subset of available properties! + (while (and lst (not foundlst)) + (if (string= (car (car lst)) f) + (setq foundlst (cdr (car lst)))) + (setq lst (cdr lst))) + (if foundlst + (setq foundlst (append foundlst matlab-core-properties)) + (setq foundlst matlab-all-known-properties)) + (setq expandto (matlab-generic-list-expand foundlst prefix next)) + ;; This looks to see if we have a singular completion. If so, + ;; then return it, and also append the "'" to the end. + (cond ((and (listp expandto) (= (length expandto) 1)) + (setq expandto (list (concat (car expandto) "'")))) + ((stringp expandto) + (setq expandto (concat expandto "'")))) + expandto)) + +(defvar matlab-last-prefix nil + "Maintained by `matlab-complete-symbol'. +The prefix used for the first completion command.") +(defvar matlab-last-semantic nil + "Maintained by `matlab-complete-symbol'. +The last type of semantic used while completing things.") +(defvar matlab-completion-search-state nil + "List of searching things we will be doing.") + +(defun matlab-complete-symbol (&optional arg) + "Complete a partially typed symbol in a MATLAB mode buffer. +If the previously entered command was also `matlab-complete-symbol' +then undo the last completion, and find a new one. + The types of symbols tried are based on the semantics of the current +cursor position. There are two types of symbols. For example, if the +cursor is in an if statement, boolean style functions and symbols are +tried first. If the line is blank, then flow control, or high level +functions are tried first. + The completion technique is controlled with `matlab-completion-technique' +It defaults to incremental completion described above. If a +completion list is preferred, then change this to 'complete. If you +just want a completion list once, then use the universal argument ARG +to change it temporarily." + (interactive "P") + (matlab-navigation-syntax + (let* ((prefix (if (and (not (eq last-command 'matlab-complete-symbol)) + (member (preceding-char) '(? ?\t ?\n ?, ?\( ?\[ ?\'))) + "" + (buffer-substring-no-properties + (save-excursion (forward-word -1) (point)) + (point)))) + (sem (matlab-lattr-semantics prefix)) + (matlab-completion-technique + (if arg (cond ((eq matlab-completion-technique 'complete) + 'increment) + (t 'complete)) + matlab-completion-technique))) + (if (not (eq last-command 'matlab-complete-symbol)) + (setq matlab-last-prefix prefix + matlab-last-semantic sem + matlab-completion-search-state + (cond ((eq sem 'solo) + '(matlab-solo-completions + matlab-find-user-functions + matlab-find-recent-variable)) + ((eq sem 'boolean) + '(matlab-find-recent-variable + matlab-boolean-completions + matlab-find-user-functions + matlab-value-completions)) + ((eq sem 'value) + '(matlab-find-recent-variable + matlab-find-user-functions + matlab-value-completions + matlab-boolean-completions)) + ((eq sem 'property) + '(matlab-property-completions + matlab-find-user-functions + matlab-find-recent-variable + matlab-value-completions)) + (t '(matlab-find-recent-variable + matlab-find-user-functions + matlab-value-completions + matlab-boolean-completions))))) + (cond + ((eq matlab-completion-technique 'increment) + (let ((r nil) (donext (eq last-command 'matlab-complete-symbol))) + (while (and (not r) matlab-completion-search-state) + (message "Expand with %S" (car matlab-completion-search-state)) + (setq r (funcall (car matlab-completion-search-state) + matlab-last-prefix donext)) + (if (not r) (setq matlab-completion-search-state + (cdr matlab-completion-search-state) + donext nil))) + (delete-region (point) (progn (forward-char (- (length prefix))) + (point))) + (if r + (insert r) + (insert matlab-last-prefix) + (message "No completions.")))) + ((eq matlab-completion-technique 'complete) + (let ((allsyms (apply 'append + (mapcar (lambda (f) (funcall f prefix)) + matlab-completion-search-state)))) + (cond ((null allsyms) + (message "No completions.") + (ding)) + ((= (length allsyms) 1) + (delete-region (point) (progn + (forward-char (- (length prefix))) + (point))) + (insert (car allsyms))) + ((= (length allsyms) 0) + (message "No completions.")) + (t + (let* ((al (mapcar (lambda (a) (list a)) allsyms)) + (c (try-completion prefix al))) + ;; This completion stuff lets us expand as much as is + ;; available to us. When the completion is the prefix + ;; then we want to display all the strings we've + ;; encountered. + (if (and (stringp c) (not (string= prefix c))) + (progn + (delete-region + (point) + (progn (forward-char (- (length prefix))) + (point))) + (insert c)) + ;; `display-completion-list' does all the complex + ;; ui work for us. + (with-output-to-temp-buffer "*Completions*" + (display-completion-list + (matlab-uniquafy-list allsyms))))))))))))) + +(defun matlab-insert-end-block (&optional reindent) + "Insert and END block based on the current syntax. +Optional argument REINDENT indicates if the specified block should be re-indented." + (interactive "P") + (if (not (matlab-ltype-empty)) (progn (end-of-line) (insert "\n"))) + (let ((valid t) (begin nil)) + (save-excursion + (condition-case nil + (progn + (matlab-backward-sexp t) + (setq begin (point) + valid (buffer-substring-no-properties + (point) (save-excursion + (re-search-forward "[\n,;.]" nil t) + (point))))) + (error (setq valid nil)))) + (if (not valid) + (error "No block to end") + (insert "end") + (if (stringp valid) (insert " % " valid)) + (matlab-indent-line) + (if reindent (indent-region begin (point) nil))))) + +(tempo-define-template + "matlab-for" + '("for " p "=" p "," > n> + r> & + "end" > %) + "for" + "Insert a MATLAB for statement" + 'matlab-tempo-tags + ) + +(tempo-define-template + "matlab-while" + '("while (" p ")," > n> + r> & + "end" > %) + "while" + "Insert a MATLAB while statement" + 'matlab-tempo-tags + ) + +(tempo-define-template + "matlab-if" + '("if " p > n + r> + "end" > n) + "if" + "Insert a MATLAB if statement" + 'matlab-tempo-tags + ) + +(tempo-define-template + "matlab-if-else" + '("if " p > n + r> + "else" > n + "end" > n) + "if" + "Insert a MATLAB if statement" + 'matlab-tempo-tags + ) + +(tempo-define-template + "matlab-try" + '("try " > n + r> + "catch" > n + p > n + "end" > n) + "try" + "Insert a MATLAB try catch statement" + 'matlab-tempo-tags + ) + +(tempo-define-template + "matlab-switch" + '("switch " p > n + "otherwise" > n + r> + "end" > n) + "switch" + "Insert a MATLAB switch statement with region in the otherwise clause." + 'matlab-tempo-tags) + +(defun matlab-insert-next-case () + "Insert a case statement inside this switch statement." + (interactive) + ;; First, make sure we are where we think we are. + (let ((valid t)) + (save-excursion + (condition-case nil + (progn + (matlab-backward-sexp t) + (setq valid (looking-at "switch"))) + (error (setq valid nil)))) + (if (not valid) + (error "Not in a switch statement"))) + (if (not (matlab-ltype-empty)) (progn (end-of-line) (insert "\n"))) + (indent-to 0) + (insert "case ") + (matlab-indent-line)) + +(tempo-define-template + "matlab-function" + '("function " + (P "output argument(s): " output t) + ;; Insert brackets only if there is more than one output argument + (if (string-match "," (tempo-lookup-named 'output)) + '(l "[" (s output) "]") + '(l (s output))) + ;; Insert equal sign only if there is output argument(s) + (if (= 0 (length (tempo-lookup-named 'output))) nil + " = ") + ;; The name of a function, as defined in the first line, should + ;; be the same as the name of the file without .m extension + (if (= 1 (count-lines 1 (point))) + (tempo-save-named + 'fname + (file-name-nondirectory (file-name-sans-extension + (buffer-file-name)))) + '(l (P "function name: " fname t))) + (tempo-lookup-named 'fname) + "(" (P "input argument(s): ") ")" n + "% " (upcase (tempo-lookup-named 'fname)) " - " (P "H1 line: ") n + "% " p n + (if matlab-functions-have-end + '(l "end" n))) + "function" + "Insert a MATLAB function statement" + 'matlab-tempo-tags + ) + +(defun matlab-stringify-region (begin end) + "Put MATLAB 's around region, and quote all quotes in the string. +Stringification allows you to type in normal MATLAB code, mark it, and +then turn it into a MATLAB string that will output exactly what's in +the region. BEGIN and END mark the region to be stringified." + (interactive "r") + (save-excursion + (goto-char begin) + (if (re-search-forward "\n" end t) + (error + "You may only stringify regions that encompass less than one line")) + (let ((m (make-marker))) + (move-marker m end) + (goto-char begin) + (insert "'") + (while (re-search-forward "'" m t) + (insert "'")) + (goto-char m) + (insert "'")))) + +(defun matlab-ispell-strings-region (begin end) + "Spell check valid strings in region with Ispell. +Argument BEGIN and END mark the region boundary." + (interactive "r") + (require 'ispell) + (save-excursion + (goto-char begin) + ;; Here we use the font lock function for finding strings. + ;; Its cheap, fast, and accurate. + (while (and (matlab-font-lock-string-match-normal end) + (ispell-region (match-beginning 2) (match-end 2)))))) + +(defun matlab-ispell-strings () + "Spell check valid strings in the current buffer with Ispell. +Calls `matlab-ispell-strings-region'" + (interactive) + (matlab-ispell-strings-region (point-min) (point-max))) + +(defun matlab-ispell-comments (&optional arg) + "Spell check comments in the current buffer with Ispell. +Optional ARG means to only check the current comment." + (interactive "P") + (let ((beg (point-min)) + (end (point-max))) + (if (and arg (matlab-ltype-comm)) + (setq beg (save-excursion (matlab-beginning-of-command) (point)) + end (save-excursion (matlab-end-of-command) (point)))) + (save-excursion + (goto-char beg) + (beginning-of-line) + (while (and (matlab-font-lock-comment-match end) + (ispell-region (match-beginning 1) (match-end 1))))))) + +(defun matlab-generate-latex () + "Convert a MATLAB M file into a Latex document for printing. +Author: Uwe Brauer oub@eucmos.sim.ucm.es +Created: 14 Feb 2002" + (interactive "*") + (save-restriction + (save-excursion + (goto-char (point-min)) + (insert "\\documentclass[12pt]{report}\n +\\usepackage{listings} +\\lstloadlanguages{Matlab} +\\lstset{language=Matlab,keywordstyle=\\bfseries,labelstep=1,escapechar=\\#} +\\begin{document} +\\begin{lstlisting}{}") + (newline) + (goto-char (point-max)) + (insert "\n\\end{lstlisting}\n\\end{document}") + (widen))) + (font-lock-mode nil) + (LaTeX-mode) + (font-lock-mode nil)) + + +;;; Block highlighting ======================================================== + +(defvar matlab-block-highlighter-timer nil + "The timer representing the block highlighter.") + +(defun matlab-enable-block-highlighting (&optional arg) + "Start or stop the block highlighter. +Optional ARG is 1 to force enable, and -1 to disable. +If ARG is nil, then highlighting is toggled." + (interactive "P") + (if (not (fboundp 'matlab-run-with-idle-timer)) + (setq matlab-highlight-block-match-flag nil)) + ;; Only do it if it's enabled. + (if (not matlab-highlight-block-match-flag) + nil + ;; Use post command idle hook as a local hook to dissuade too much + ;; cpu time while doing other things. + ;;(make-local-hook 'post-command-hook) + (if (not arg) + (setq arg + (if (member 'matlab-start-block-highlight-timer + post-command-hook) + -1 1))) + (if (> arg 0) + (add-hook 'post-command-hook 'matlab-start-block-highlight-timer) + (remove-hook 'post-command-hook 'matlab-start-block-highlight-timer)))) + +(defvar matlab-block-highlight-overlay nil + "The last highlighted overlay.") +(make-variable-buffer-local 'matlab-block-highlight-overlay) + +(defvar matlab-block-highlight-timer nil + "Last started timer.") +(make-variable-buffer-local 'matlab-block-highlight-timer) + +(defun matlab-start-block-highlight-timer () + "Set up a one-shot timer if we are in MATLAB mode." + (if (eq major-mode 'matlab-mode) + (progn + (if matlab-block-highlight-overlay + (unwind-protect + (matlab-delete-overlay matlab-block-highlight-overlay) + (setq matlab-block-highlight-overlay nil))) + (if matlab-block-highlight-timer + (unwind-protect + (matlab-cancel-timer matlab-block-highlight-timer) + (setq matlab-block-highlight-timer nil))) + (setq matlab-block-highlight-timer + (matlab-run-with-idle-timer + 1 nil 'matlab-highlight-block-match + (current-buffer)))))) + +(defun matlab-highlight-block-match (&optional buff-when-launched) + "Highlight a matching block if available. +BUFF-WHEN-LAUNCHED is the buffer that was active when the timer was set." + (setq matlab-block-highlight-timer nil) + (if (null buff-when-launched) + ;; We were passed a null. This indicates an old version of XEmacs + ;; so just turn the feature off + (setq matlab-highlight-block-match-flag nil) + ;; Only do neat stuff in the same buffer as the one we were + ;; initialized from. + (when (and buff-when-launched + (eq buff-when-launched (current-buffer))) + (let ((inhibit-quit nil) ;turn on G-g + (matlab-scan-on-screen-only t)) + (if matlab-show-periodic-code-details-flag + (matlab-show-line-info)) + (if (not (matlab-cursor-in-string-or-comment)) + (save-excursion + (if (or (bolp) + (looking-at "\\s-") + (save-excursion (forward-char -1) (looking-at "\\s-"))) + nil + (forward-word -1)) + (if (and (looking-at (concat (matlab-block-beg-re) "\\>")) + (not (looking-at "function"))) + (progn + ;; We scan forward... + (matlab-forward-sexp) + (backward-word 1) + (if (not (looking-at matlab-block-end-pre-if)) + nil ;(message "Unterminated block, or end off screen.") + (setq matlab-block-highlight-overlay + (matlab-make-overlay (point) + (progn (forward-word 1) + (point)) + (current-buffer))) + (matlab-overlay-put matlab-block-highlight-overlay + 'face 'matlab-region-face))) + (if (and (looking-at (concat (matlab-block-end-pre) "\\>")) + (not (looking-at "function")) + (matlab-valid-end-construct-p)) + (progn + ;; We scan backward + (forward-word 1) + (condition-case nil + (progn + (matlab-backward-sexp) + (if (not (looking-at (matlab-block-beg-re))) + nil ;(message "Unstarted block at cursor.") + (setq matlab-block-highlight-overlay + (matlab-make-overlay (point) + (progn (forward-word 1) + (point)) + (current-buffer))) + (matlab-overlay-put matlab-block-highlight-overlay + 'face 'matlab-region-face))) + (error (message "Unstarted block at cursor.")))) + ;; do nothing + )))))))) + + +;;; M Block Folding with hideshow ============================================= + +(defun matlab-hideshow-forward-sexp-func (arg) + "Move forward one sexp for hideshow. +Argument ARG specifies the number of blocks to move forward." + (beginning-of-line) + (matlab-forward-sexp arg) + ) + +(defun matlab-hideshow-adjust-beg-func (arg) + "Adjust the beginning of a hideshow block. +Argument ARG to make it happy." + (end-of-line) + (point) + ) + +;; Use this to enable hideshow in MATLAB. +;; It has not been tested by me enough. + +;; REMOVE PUSHNEW FROM THIS LINE +;;(pushnew (list 'matlab-mode +;; (matlab-block-beg-pre) +;; (matlab-block-end-pre) +;; "%" +;; 'matlab-hideshow-forward-sexp-func +;; 'matlab-hideshow-adjust-beg-func +;; ) +;; hs-special-modes-alist :test 'equal) + + +;;; M Code verification & Auto-fix ============================================ + +(defun matlab-mode-verify-fix-file-fn () + "Verify the current buffer from `write-contents-hooks'." + (if matlab-verify-on-save-flag + (matlab-mode-verify-fix-file (> (point-max) + matlab-block-verify-max-buffer-size))) + ;; Always return nil. + nil) + +(defun matlab-mode-verify-fix-file (&optional fast) + "Verify the current buffer satisfies all M things that might be useful. +We will merely loop across a list of verifiers/fixers in +`matlab-mode-verify-fix-functions'. +If optional FAST is non-nil, do not perform usually lengthy checks." + (interactive) + (let ((p (point)) + (l matlab-mode-verify-fix-functions)) + (while l + (funcall (car l) fast) + (setq l (cdr l))) + (goto-char p)) + (if (interactive-p) + (message "Done."))) + +(defun matlab-toggle-show-mlint-warnings () + "Toggle `matlab-show-mlint-warnings'." + (interactive) + (setq matlab-show-mlint-warnings (not matlab-show-mlint-warnings)) + (if matlab-highlight-cross-function-variables + (if matlab-show-mlint-warnings + (mlint-buffer) ; became true, recompute mlint info + (mlint-clear-warnings)) ; became false, just remove hilighting + (mlint-minor-mode))) ; change mlint mode altogether + +(defun matlab-toggle-highlight-cross-function-variables () + "Toggle `matlab-highlight-cross-function-variables'." + (interactive) + (setq matlab-highlight-cross-function-variables + (not matlab-highlight-cross-function-variables)) + (if matlab-show-mlint-warnings + (if matlab-highlight-cross-function-variables + (mlint-buffer) ; became true, recompute mlint info + ; became false, just remove hilighting ... + (mlint-clear-cross-function-variable-highlighting)) + (mlint-minor-mode))) ; change mlint mode altogether + +;; +;; Add more auto verify/fix functions here! +;; +(defun matlab-mode-vf-functionname (&optional fast) + "Verify/Fix the function name of this file. +Optional argument FAST is ignored." + (matlab-navigation-syntax + (goto-char (point-min)) + (while (and (or (matlab-ltype-empty) (matlab-ltype-comm)) + (/= (matlab-point-at-eol) (point-max))) + (forward-line 1)) + (let ((func nil) + (bn (file-name-sans-extension + (file-name-nondirectory (buffer-file-name))))) + (if (looking-at (matlab-match-function-re)) + ;; The expression above creates too many numeric matches + ;; to apply a known one to our function. We cheat by knowing that + ;; match-end 0 is at the end of the function name. We can then go + ;; backwards, and get the extents we need. Navigation syntax + ;; lets us know that backward-word really covers the word. + (let ((end (match-end 0)) + (begin (progn (goto-char (match-end 0)) + (forward-word -1) + (point)))) + (setq func (buffer-substring begin end)) + (if (not (string= func bn)) + (if (not (matlab-mode-highlight-ask + begin end + "Function and file names are different. Fix?")) + nil + (goto-char begin) + (delete-region begin end) + (insert bn)))))))) + +(defun matlab-mode-vf-block-matches-forward (&optional fast) + "Verify/Fix unterminated (or un-ended) blocks. +This only checks block regions like if/end. +Optional argument FAST causes this check to be skipped." + (goto-char (point-min)) + (let ((go t) + (expr (concat "\\<\\(" (matlab-block-beg-pre) "\\)\\>"))) + (matlab-navigation-syntax + (while (and (not fast) go (re-search-forward expr nil t)) + (forward-word -1) ;back over the special word + (let ((s (point))) + (condition-case nil + (if (and (not (matlab-cursor-in-string-or-comment)) + (not (looking-at "function"))) + (progn + (matlab-forward-sexp) + (forward-word -1) + (if (not (looking-at + (concat matlab-block-end-pre-no-if "\\>"))) + (setq go nil))) + (forward-word 1)) + (error (setq go nil))) + (if (and (not go) (goto-char s) + (not (matlab-mode-highlight-ask + (point) (save-excursion (forward-word 1) (point)) + "Unterminated block. Continue anyway?"))) + (error "Unterminated Block found!"))) + (message "Block-check: %d%%" (/ (/ (* 100 (point)) (point-max)) 2)))))) + +(defun matlab-mode-vf-block-matches-backward (&optional fast) + "Verify/fix unstarted (or dangling end) blocks. +Optional argument FAST causes this check to be skipped." + (goto-char (point-max)) + (let ((go t) (expr (concat "\\<\\(" (matlab-block-end-no-function-re) + "\\)\\>"))) + (matlab-navigation-syntax + (while (and (not fast) go (re-search-backward expr nil t)) + (forward-word 1) + (let ((s (point))) + (condition-case nil + (if (and (not (matlab-cursor-in-string-or-comment)) + (matlab-valid-end-construct-p)) + (matlab-backward-sexp) + (backward-word 1)) + (error (setq go nil))) + (if (and (not go) (goto-char s) + (not (matlab-mode-highlight-ask + (point) (save-excursion (backward-word 1) (point)) + "Unstarted block. Continue anyway?"))) + (error "Unstarted Block found!"))) + (message "Block-check: %d%%" + (+ (/ (/ (* 100 (- (point-max) (point))) (point-max)) 2) 50)))))) + +;;; Utility for verify/fix actions if you need to highlight +;; a section of the buffer for the user's approval. +(defun matlab-mode-highlight-ask (begin end prompt) + "Highlight from BEGIN to END while asking PROMPT as a yes-no question." + (let ((mo (matlab-make-overlay begin end (current-buffer))) + (ans nil)) + (condition-case nil + (progn + (matlab-overlay-put mo 'face 'matlab-region-face) + (setq ans (y-or-n-p prompt)) + (matlab-delete-overlay mo)) + (quit (matlab-delete-overlay mo) (error "Quit"))) + ans)) + +;;; Quiesce an M file to remove accidental display of ANS during a run. +;; Useful if you have random outputs and you don't know where they are from, +;; or before compiling to standalone where some functions now have outputs +;; that did not have outputs earlier. +;; +;; You probably don't want this as a default verify function +(defvar matlab-quiesce-nosemi-regexp "\\s-*\\(function\\|parfor\\|for\\|spmd\\|while\\|try\\|catch\\|\ +switch\\|otherwise\\|case\\|break\\|if\\|else\\|end\\|return\\|disp\\|\ +$\\|%\\)" + "Regular expression used to detect if a semicolon is needed at the end of a line.") + +(defun matlab-mode-vf-quiesce-buffer (&optional fast) + "Find all commands that do not end in ;, and add one. +This has the effect of removing any extraneous output that may not be +desired. Optional argument FAST is not used." + (interactive) + (save-excursion + (push-mark) + (goto-char (point-min)) + (let ((msgpos 0) (dir .2)) + (while (not (save-excursion (end-of-line) (eobp))) + (message (aref [ "Scanning o...." "Scanning .o..." "Scanning ..o.." + "Scanning ...o." "Scanning ....o" ] (floor msgpos))) + (setq msgpos (+ msgpos dir)) + (if (or (> msgpos 5) (< msgpos 0)) (setq dir (- dir) + msgpos (+ (* 2 dir) msgpos))) + (matlab-end-of-command (point)) + (if (matlab-cursor-in-comment) + (progn + (matlab-comment-on-line) + (skip-chars-backward " \t"))) + (if (and (not (= (preceding-char) ?\;)) + (not (matlab-cursor-in-string t)) + (not (save-excursion + (beginning-of-line) + (looking-at matlab-quiesce-nosemi-regexp)))) + (let ((p (point))) + (skip-chars-backward " \t") + (if (/= p (point)) + (progn + (delete-region p (point)) + (forward-line -1)) + (if (matlab-mode-highlight-ask (point) (+ 1 (point)) + "Add Semi colon here? ") + (insert ";"))))) + (forward-line 1)))) + (message "Scanning .... done")) + + + +;;; V19 stuff ================================================================= + +(defvar matlab-mode-menu-keymap nil + "Keymap used in MATLAB mode to provide a menu.") + +(defun matlab-frame-init () + "Initialize Emacs 19+ menu system." + (interactive) + ;; make a menu keymap + (easy-menu-define + matlab-mode-menu + matlab-mode-map + "MATLAB menu" + '("MATLAB" + ["Start MATLAB" matlab-shell (not (matlab-with-emacs-link)) ] + ["Save and go" matlab-shell-save-and-go t] + ["Run Region" matlab-shell-run-region t] + ["Run Cell" matlab-shell-run-cell t] + ["Version" matlab-show-version t] + "----" + ["Find M file" matlab-find-file-on-path t] + ["Show M-Lint Warnings" matlab-toggle-show-mlint-warnings + :active (and (locate-library "mlint") (fboundp 'mlint-minor-mode)) + :style toggle :selected matlab-show-mlint-warnings + ] + ("Auto Fix" + ["Verify/Fix source" matlab-mode-verify-fix-file t] + ["Spell check strings" matlab-ispell-strings t] + ["Spell check comments" matlab-ispell-comments t] + ["Quiesce source" matlab-mode-vf-quiesce-buffer t] + ) + ("Navigate" + ["Beginning of Command" matlab-beginning-of-command t] + ["End of Command" matlab-end-of-command t] + ["Forward Block" matlab-forward-sexp t] + ["Backward Block" matlab-backward-sexp t] + ["Beginning of Function" matlab-beginning-of-defun t] + ["End of Function" matlab-end-of-defun t]) + ("Format" + ["Justify Line" matlab-justify-line t] + ["Fill Region" matlab-fill-region t] + ["Fill Comment Paragraph" matlab-fill-paragraph + (save-excursion (matlab-comment-on-line))] + ["Join Comment" matlab-join-comment-lines + (save-excursion (matlab-comment-on-line))] + ["Comment Region" matlab-comment-region t] + ["Uncomment Region" matlab-uncomment-region t] + ["Indent Synactic Block" matlab-indent-sexp]) + ("Insert" + ["Complete Symbol" matlab-complete-symbol t] + ["Comment" matlab-comment t] + ["if end" tempo-template-matlab-if t] + ["if else end" tempo-template-matlab-if-else t] + ["for end" tempo-template-matlab-for t] + ["switch otherwise end" tempo-template-matlab-switch t] + ["Next case" matlab-insert-next-case t] + ["try catch end" tempo-template-matlab-try t] + ["while end" tempo-template-matlab-while t] + ["End of block" matlab-insert-end-block t] + ["Function" tempo-template-matlab-function t] + ["Stringify Region" matlab-stringify-region t] + ) + ("Customize" +; ["Auto Fill Counts Elipsis" +; (lambda () (setq matlab-fill-count-ellipsis-flag +; (not matlab-fill-count-ellipsis-flag))) +; :style toggle :selected 'matlab-fill-count-ellipsis-flag] + ["Indent Function Body" + (setq matlab-indent-function-body (not matlab-indent-function-body)) + :style toggle :selected matlab-indent-function-body] + ["Functions Have end" + matlab-toggle-functions-have-end + :style toggle :selected matlab-functions-have-end] + ["Verify File on Save" + (setq matlab-verify-on-save-flag (not matlab-verify-on-save-flag)) + :style toggle :selected matlab-verify-on-save-flag] + ["Auto Fill does Code" + (setq matlab-fill-code (not matlab-fill-code)) + :style toggle :selected matlab-fill-code ] + ["Periodic Code Details" + (setq matlab-show-periodic-code-details-flag + (not matlab-show-periodic-code-details-flag)) + :style toggle :selected matlab-show-periodic-code-details-flag ] + ["Highlight Matching Blocks" + (matlab-enable-block-highlighting) + :style toggle :selected (member 'matlab-start-block-highlight-timer + post-command-hook) ] + ["Highlight Cross-Function Variables" + matlab-toggle-highlight-cross-function-variables + :active (locate-library "mlint") + :style toggle :selected matlab-highlight-cross-function-variables + ] + ["Add Needed Semicolon on RET" + (setq matlab-return-add-semicolon (not matlab-return-add-semicolon)) + :style toggle :selected matlab-return-add-semicolon + ] + ["Customize" (customize-group 'matlab) + (and (featurep 'custom) (fboundp 'custom-declare-variable)) + ] + ) + "----" + ["Run M Command" matlab-shell-run-command (matlab-shell-active-p)] + ["Describe Command" matlab-shell-describe-command (matlab-shell-active-p)] + ["Describe Variable" matlab-shell-describe-variable (matlab-shell-active-p)] + ["Command Apropos" matlab-shell-apropos (matlab-shell-active-p)] + ["Topic Browser" matlab-shell-topic-browser (matlab-shell-active-p)] + )) + (easy-menu-add matlab-mode-menu matlab-mode-map)) + +;;; MATLAB shell ============================================================= + +(defgroup matlab-shell nil + "MATLAB shell mode." + :prefix "matlab-shell-" + :group 'matlab) + +(defcustom matlab-shell-command "matlab" + "*The name of the command to be run which will start the MATLAB process." + :group 'matlab-shell + :type 'string) + +(defcustom matlab-shell-command-switches '("-nodesktop") + "*Command line parameters run with `matlab-shell-command'. +Command switches are a list of strings. Each entry is one switch." + :group 'matlab-shell + :type '(list :tag "Switch: ")) + +(defcustom matlab-shell-echoes t + "*If `matlab-shell-command' echoes input." + :group 'matlab-shell + :type 'boolean) + +(defvar matlab-shell-running-matlab-version nil + "The version of MATLAB running in the current `matlab-shell' buffer.") +(defvar matlab-shell-running-matlab-release nil + "The release of MATLAB running in the curbrent `matlab-shell' buffer.") +(defvar matlab-shell-use-emacs-toolbox + (let ((dir (expand-file-name "toolbox/emacsinit.m" + (file-name-directory (locate-library "matlab"))))) + (file-exists-p dir)) + "Add the `matlab-shell' MATLAB toolbox to the MATLAB path on startup.") +(defvar matlab-shell-emacsclient-command "emacsclient -n" + "The command to use as an external editor for MATLAB. +Using emacsclient allows the currently running Emacs to also be the +external editor for MATLAB.") + +(defcustom matlab-shell-history-file "~/.matlab/%s/history.m" + "*Location of the history file. +A %s is replaced with the MATLAB version release number, such as R12. +This file is read to initialize the comint input ring.") + +(defcustom matlab-shell-input-ring-size 32 + "*Number of history elements to keep." + :group 'matlab-shell + :type 'integer) + +(defcustom matlab-shell-enable-gud-flag t + "*Non-nil means to use GUD mode when running the MATLAB shell." + :group 'matlab-shell + :type 'boolean) + +(defcustom matlab-shell-mode-hook nil + "*List of functions to call on entry to MATLAB shell mode." + :group 'matlab-shell + :type 'hook) + +(defcustom matlab-shell-ask-MATLAB-for-completions t + "When Non-nil, ask MATLAB for a completion list. +When nil, just complete file names. (The original behavior.) +At this time, MATLAB based completion can be slow if there are +a lot of possible answers." + :group 'matlab-shell + :type 'boolean) + +(defvar matlab-shell-buffer-name "MATLAB" + "Name used to create `matlab-shell' mode buffers. +This name will have *'s surrounding it.") + +(defun matlab-shell-active-p () + "Return t if the MATLAB shell is active." + (if (get-buffer (concat "*" matlab-shell-buffer-name "*")) + (save-excursion + (set-buffer (concat "*" matlab-shell-buffer-name "*")) + (if (comint-check-proc (current-buffer)) + (current-buffer))))) + +(defvar matlab-shell-mode-map () + "Keymap used in `matlab-shell-mode'.") + +(defvar matlab-shell-font-lock-keywords-1 + (append matlab-font-lock-keywords matlab-shell-font-lock-keywords) + "Keyword symbol used for font-lock mode.") + +(defvar matlab-shell-font-lock-keywords-2 + (append matlab-shell-font-lock-keywords-1 matlab-gaudy-font-lock-keywords) + "Keyword symbol used for gaudy font-lock symbols.") + +(defvar matlab-shell-font-lock-keywords-3 + (append matlab-shell-font-lock-keywords-2 + matlab-really-gaudy-font-lock-keywords) + "Keyword symbol used for really gaudy font-lock symbols.") + +(defvar matlab-prompt-seen nil + "Track visibility of MATLAB prompt in MATLAB Shell.") + +(eval-when-compile (require 'gud) (require 'comint) (require 'shell)) + +;;;###autoload +(defun matlab-shell () + "Create a buffer with MATLAB running as a subprocess. + +MATLAB shell cannot work on the MS Windows platform because MATLAB is not +a console application." + (interactive) + ;; MATLAB shell does not work by default on the Windows platform. Only + ;; permit it's operation when the shell command string is different from + ;; the default value. (True when the engine program is running.) + (if (and (or (eq window-system 'pc) (eq window-system 'w32)) + (string= matlab-shell-command "matlab")) + (error "MATLAB cannot be run as a inferior process. \ +Try C-h f matlab-shell RET")) + + (require 'shell) + (require 'gud) + + ;; Make sure this is safe... + (if (and matlab-shell-enable-gud-flag (fboundp 'gud-def)) + ;; We can continue using GUD + nil + (message "Sorry, your emacs cannot use the MATLAB Shell GUD features.") + (setq matlab-shell-enable-gud-flag nil)) + + (switch-to-buffer (concat "*" matlab-shell-buffer-name "*")) + (if (matlab-shell-active-p) + nil + ;; Clean up crufty state + (kill-all-local-variables) + ;; Build keymap here in case someone never uses comint mode + (if matlab-shell-mode-map + () + (setq matlab-shell-mode-map + (let ((km (make-sparse-keymap 'matlab-shell-mode-map))) + (if (fboundp 'set-keymap-parent) + (set-keymap-parent km comint-mode-map) + ;; 19.31 doesn't have set-keymap-parent + (setq km (nconc km comint-mode-map))) + (substitute-key-definition 'next-error 'matlab-shell-last-error + km global-map) + (define-key km [(control h) (control m)] + matlab-help-map) + (define-key km "\C-c." 'matlab-find-file-on-path) + (define-key km [(tab)] 'matlab-shell-tab) + (define-key km [(control up)] + 'comint-previous-matching-input-from-input) + (define-key km [(control down)] + 'comint-next-matching-input-from-input) + (define-key km [up] + 'matlab-shell-previous-matching-input-from-input) + (define-key km [down] + 'matlab-shell-next-matching-input-from-input) + (define-key km [(control return)] 'comint-kill-input) + (define-key km "\C-?" + 'matlab-shell-delete-backwards-no-prompt) + (define-key km [(backspace)] + 'matlab-shell-delete-backwards-no-prompt) + km))) + (switch-to-buffer + (apply 'make-comint matlab-shell-buffer-name matlab-shell-command + nil matlab-shell-command-switches)) + + (setq shell-dirtrackp t) + (comint-mode) + + (if matlab-shell-enable-gud-flag + (progn + (gud-mode) + (make-local-variable 'matlab-prompt-seen) + (setq matlab-prompt-seen nil) + (make-local-variable 'gud-marker-filter) + (setq gud-marker-filter 'gud-matlab-marker-filter) + (make-local-variable 'gud-find-file) + (setq gud-find-file 'gud-matlab-find-file) + + (set-process-filter (get-buffer-process (current-buffer)) + 'gud-filter) + (set-process-sentinel (get-buffer-process (current-buffer)) + 'gud-sentinel) + (gud-set-buffer)) + ;; What to do when there is no GUD + ;(set-process-filter (get-buffer-process (current-buffer)) + ; 'matlab-shell-process-filter) + ) + + ;; Comint and GUD both try to set the mode. Now reset it to + ;; matlab mode. + (matlab-shell-mode))) + +(defcustom matlab-shell-logo + (if (fboundp 'locate-data-file) + ;; Starting from XEmacs 20.4 use locate-data-file + (locate-data-file "matlab.xpm") + (expand-file-name "matlab.xpm" data-directory)) + "*The MATLAB logo file." + :group 'matlab-shell + :type '(choice (const :tag "None" nil) + (file :tag "File" ""))) + + +(defun matlab-shell-hack-logo (str) + "Replace the text logo with a real logo. +STR is passed from the commint filter." + (when (string-match "< M A T L A B >" str) + (save-excursion + (when (re-search-backward "^[ \t]+< M A T L A B (R) >" (point-min) t) + (delete-region (match-beginning 0) (match-end 0)) + (insert (make-string 16 ? )) + (set-extent-begin-glyph (make-extent (point) (point)) + (make-glyph matlab-shell-logo)))) + ;; Remove this function from `comint-output-filter-functions' + (remove-hook 'comint-output-filter-functions + 'matlab-shell-hack-logo)) + + ) + +(defun matlab-shell-version-scrape (str) + "Scrape the MATLAB Version from the MATLAB startup text. +Argument STR is the string to examine for version information." + (when (string-match "\\(Version\\)\\s-+\\([.0-9]+\\)\\s-+(\\(R[.0-9]+[ab]?\\))" str) + ;; Extract the release number + (setq matlab-shell-running-matlab-version + (match-string 2 str) + matlab-shell-running-matlab-release + (match-string 3 str)) + ;; Now get our history loaded + (setq comint-input-ring-file-name + (format matlab-shell-history-file matlab-shell-running-matlab-release)) + (if (fboundp 'comint-read-input-ring) + (comint-read-input-ring t)) + ;; Remove the scrape from our list of things to do. + (remove-hook 'comint-output-filter-functions + 'matlab-shell-version-scrape))) + +(defun matlab-shell-mode () + "Run MATLAB as a subprocess in an Emacs buffer. + +This mode will allow standard Emacs shell commands/completion to occur +with MATLAB running as an inferior process. Additionally, this shell +mode is integrated with `matlab-mode', a major mode for editing M +code. + +> From an M file buffer: +\\<matlab-mode-map> +\\[matlab-shell-save-and-go] - Save the current M file, and run it in a \ +MATLAB shell. + +> From Shell mode: +\\<matlab-shell-mode-map> +\\[matlab-shell-last-error] - find location of last MATLAB runtime error \ +in the offending M file. + +> From an M file, or from Shell mode: +\\<matlab-mode-map> +\\[matlab-shell-run-command] - Run COMMAND and show result in a popup buffer. +\\[matlab-shell-describe-variable] - Show variable contents in a popup buffer. +\\[matlab-shell-describe-command] - Show online documentation for a command \ +in a popup buffer. +\\[matlab-shell-apropos] - Show output from LOOKFOR command in a popup buffer. +\\[matlab-shell-topic-browser] - Topic browser using HELP. + +> Keymap: +\\{matlab-mode-map}" + (setq major-mode 'matlab-shell-mode + mode-name "M-Shell" + comint-prompt-regexp "^\\(K\\|EDU\\)?>> *" + comint-delimiter-argument-list (list [ 59 ]) ; semi colon + comint-dynamic-complete-functions '(comint-replace-by-expanded-history) + comint-process-echoes matlab-shell-echoes + ) + (require 'shell) + (if (fboundp 'shell-directory-tracker) + (add-hook 'comint-input-filter-functions 'shell-directory-tracker)) + ;; Add a spiffy logo if we are running XEmacs + (if (and (string-match "XEmacs" emacs-version) + (stringp matlab-shell-logo) + (file-readable-p matlab-shell-logo)) + (add-hook 'comint-output-filter-functions 'matlab-shell-hack-logo)) + ;; Add a version scraping logo identification filter. + (add-hook 'comint-output-filter-functions 'matlab-shell-version-scrape) + ;; Add pseudo html-renderer + (add-hook 'comint-output-filter-functions 'matlab-shell-render-html-anchor nil t) + (add-hook 'comint-output-filter-functions 'matlab-shell-render-errors-as-anchor nil t) + + (make-local-variable 'comment-start) + (setq comment-start "%") + (use-local-map matlab-shell-mode-map) + (set-syntax-table matlab-mode-syntax-table) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '((matlab-shell-font-lock-keywords-1 + matlab-shell-font-lock-keywords-2 + matlab-shell-font-lock-keywords-3) + t nil ((?_ . "w")))) + (set (make-local-variable 'comint-input-ring-size) + matlab-shell-input-ring-size) + (set (make-local-variable 'comint-input-ring-file-name) + (format matlab-shell-history-file "R12")) + (if (fboundp 'comint-read-input-ring) + (comint-read-input-ring t)) + (make-local-variable 'gud-marker-acc) + (easy-menu-define + matlab-shell-menu + matlab-shell-mode-map + "MATLAB shell menu" + '("MATLAB" + ["Goto last error" matlab-shell-last-error t] + "----" + ["Stop On Errors" matlab-shell-dbstop-error t] + ["Don't Stop On Errors" matlab-shell-dbclear-error t] + "----" + ["Run Command" matlab-shell-run-command t] + ["Describe Variable" matlab-shell-describe-variable t] + ["Describe Command" matlab-shell-describe-command t] + ["Lookfor Command" matlab-shell-apropos t] + ["Topic Browser" matlab-shell-topic-browser t] + "----" + ["Demos" matlab-shell-demos t] + ["Close Current Figure" matlab-shell-close-current-figure t] + ["Close Figures" matlab-shell-close-figures t] + "----" + ["Customize" (customize-group 'matlab-shell) + (and (featurep 'custom) (fboundp 'custom-declare-variable)) + ] + ["Exit" matlab-shell-exit t])) + (easy-menu-add matlab-shell-menu matlab-shell-mode-map) + + (if matlab-shell-enable-gud-flag + (progn + (gud-def gud-break "dbstop at %l in %f" "\C-b" "Set breakpoint at current line.") + (gud-def gud-remove "dbclear at %l in %f" "\C-d" "Remove breakpoint at current line") + (gud-def gud-step "dbstep in" "\C-s" "Step one source line, possibly into a function.") + (gud-def gud-next "dbstep %p" "\C-n" "Step over one source line.") + (gud-def gud-cont "dbcont" "\C-r" "Continue with display.") + (gud-def gud-finish "dbquit" "\C-f" "Finish executing current function.") + (gud-def gud-up "dbup %p" "<" "Up N stack frames (numeric arg).") + (gud-def gud-down "dbdown %p" ">" "Down N stack frames (numeric arg).") + (gud-def gud-print "%e" "\C-p" "Evaluate M expression at point.") + (if (fboundp 'gud-make-debug-menu) + (gud-make-debug-menu)) + (if (fboundp 'gud-overload-functions) + (gud-overload-functions + '((gud-massage-args . gud-matlab-massage-args) + (gud-marker-filter . gud-matlab-marker-filter) + (gud-find-file . gud-matlab-find-file)))) + ;; XEmacs doesn't seem to have this concept already. Oh well. + (setq gud-marker-acc nil) + ;; XEmacs has problems w/ this variable. Set it here. + (set-marker comint-last-output-start (point-max)) + )) + (run-hooks 'matlab-shell-mode-hook) + (matlab-show-version) + ) + +(defvar gud-matlab-marker-regexp-prefix "error:\\|opentoline" + "A prefix to scan for to know if output might be scarfed later.") + +;; The regular expression covers the following form: +;; Errors: Error in ==> <filename> +;; On line # ==> <command_name> +;; Syntax: Syntax error in ==> <filename> +;; On line # ==> <sample-text> +;; Warning: In <filename> at line # <stuff> +(defvar gud-matlab-error-regexp + (concat "\\(Error in ==>\\|Syntax error in ==>\\|In\\) " + "\\([-@.a-zA-Z_0-9/ \\\\:]+\\).*[\n ][Oa][nt]\\(?: line\\)? " + "\\([0-9]+\\) ?") + "Regular expression finding where an error occurred.") + +(defvar matlab-shell-html-map (make-sparse-keymap)) +(if (string-match "XEmacs" emacs-version) + (define-key matlab-shell-html-map [button2] 'matlab-shell-html-click) + (define-key matlab-shell-html-map [mouse-2] 'matlab-shell-html-click)) + +(defvar matlab-anchor-beg "<a href=\"\\([^\"]+\\)\">" + "Beginning of html anchor.") + +(defvar matlab-anchor-end "</a>" + "End of html anchor.") + +(defun matlab-shell-render-html-anchor (str) + "Render html anchors inserted into the MATLAB shell buffer. +Argument STR is the text for the anchor." + (if (string-match matlab-anchor-end str) + (save-excursion + (while (re-search-backward matlab-anchor-beg nil t) + (let* ((anchor-beg-start (match-beginning 0)) + (anchor-beg-finish (match-end 0)) + (anchor-text (match-string 1)) + (anchor-end-finish (search-forward matlab-anchor-end)) + (anchor-end-start (match-beginning 0)) + (o (matlab-make-overlay anchor-beg-finish anchor-end-start))) + (matlab-overlay-put o 'mouse-face 'highlight) + (matlab-overlay-put o 'face 'underline) + (matlab-overlay-put o 'matlab-url anchor-text) + (matlab-overlay-put o 'keymap matlab-shell-html-map) + (delete-region anchor-end-start anchor-end-finish) + (delete-region anchor-beg-start anchor-beg-finish) + )))) + ) + +(defvar matlab-shell-error-stack-start "^{\\?\\?\\?\\s-" + "Regexp for the start of a stack from an error. +These error stacks are for MATLAB version R2009a or so. +Disable this if anchors are ever supported.") + +(defvar matlab-shell-error-stack-end "^}\\s-*$" + "Regexp for the start of a stack from an error. +These error stacks are for MATLAB version R2009a or so. +Disable this if anchors are ever supported.") + +(defun matlab-shell-render-errors-as-anchor (str) + "Detect non-url errors, and treat them as if they were url anchors. +Argument STR is the text that might have errors in it." + (when (string-match matlab-shell-error-stack-end str) + (save-excursion + (when (re-search-backward matlab-shell-error-stack-start nil t) + (let ((groupend nil) + (first nil)) + (save-excursion + (setq groupend (re-search-forward matlab-shell-error-stack-end)) + ) + ;; We have found an error stack to investigate. + (while (re-search-forward gud-matlab-error-regexp nil t) + (let* ((err-start (match-beginning 0)) + (err-end (match-end 0)) + (err-text (match-string 0)) + (err-file (match-string 2)) + (err-line (match-string 3)) + (o (matlab-make-overlay err-start err-end)) + (url (concat "opentoline('" err-file "'," err-line ",0)")) + ) + (matlab-overlay-put o 'mouse-face 'highlight) + (matlab-overlay-put o 'face 'underline) + ;; The url will recycle opentoline code. + (matlab-overlay-put o 'matlab-url url) + (matlab-overlay-put o 'keymap matlab-shell-html-map) + ;; Keep track of the very first error in this error stack. + ;; It will represent the "place to go" for "go-to-last-error". + (matlab-overlay-put o 'first-in-error-stack first) + (when (not first) (setq first url)) + )) + + ))))) + +(defvar gud-matlab-marker-regexp-1 "^K>>" + "Regular expression for finding a file line-number.") + +(defvar gud-matlab-marker-regexp-2 + (concat "^> In \\(" matlab-anchor-beg + "\\|\\)\\([-.a-zA-Z0-9_>/@]+\\) \\((\\w+) \\|\\)at line \\([0-9]+\\)[ \n]+") + "Regular expression for finding a file line-number. +Please note: The leading > character represents the current stack frame, so if +there are several frames, this makes sure we pick the right one to popup.") + +(defun gud-matlab-massage-args (file args) + "Argument massager for starting matlab file. +I don't think I have to do anything, but I'm not sure. +FILE is ignored, and ARGS is returned." + args) + +(defun gud-matlab-marker-filter (string) + "Filters STRING for the Unified Debugger based on MATLAB output." + (if matlab-prompt-seen + nil + (when (string-match ">> " string) + (if matlab-shell-use-emacs-toolbox + ;; Use our local toolbox directory. + (process-send-string + (get-buffer-process gud-comint-buffer) + (format "addpath('%s','-begin'); rehash; emacsinit('%s');\n" + (expand-file-name "toolbox" + (file-name-directory + (locate-library "matlab"))) + matlab-shell-emacsclient-command)) + ;; User doesn't want to use our fancy toolbox directory + (process-send-string + (get-buffer-process gud-comint-buffer) + "if usejava('jvm'), \ +com.mathworks.services.Prefs.setBooleanPref('EditorGraphicalDebugging', false); \ +end\n" + )) + ;; Mark that we've seen at least one prompt. + (setq matlab-prompt-seen t) + )) + (let ((garbage (concat "\\(" (regexp-quote "\C-g") "\\|" + (regexp-quote "\C-h") "\\|" + (regexp-quote "\033[H0") "\\|" + (regexp-quote "\033[H\033[2J") "\\|" + (regexp-quote "\033H\033[2J") "\\)"))) + (while (string-match garbage string) + (if (= (aref string (match-beginning 0)) ?\C-g) + (beep t)) + (setq string (replace-match "" t t string)))) + (setq gud-marker-acc (concat gud-marker-acc string)) + (let ((output "") (frame nil)) + + (when (not frame) + (when (string-match gud-matlab-marker-regexp-1 gud-marker-acc) + (when (not frame) + ;; If there is a debug prompt, and no frame currently set, + ;; go find one. + (let ((url gud-marker-acc) + ef el) + (cond + ((string-match "^error:\\(.*\\),\\([0-9]+\\),\\([0-9]+\\)$" url) + (setq ef (substring url (match-beginning 1) (match-end 1)) + el (substring url (match-beginning 2) (match-end 2))) + ) + ((string-match "opentoline('\\([^']+\\)',\\([0-9]+\\),\\([0-9]+\\))" url) + (setq ef (substring url (match-beginning 1) (match-end 1)) + el (substring url (match-beginning 2) (match-end 2))) + ) + ) + (when ef + (setq frame (cons ef (string-to-number el))))))) + ) + + (if (and (not frame) + (string-match gud-matlab-marker-regexp-prefix gud-marker-acc) + (not (string-match "^K?>>" gud-marker-acc)) + ) + ;; We could be collecting something. Wait for a while. + nil + ;; Finish off this part of the output. None of our special stuff + ;; ends with a \n, so display those as they show up... + (while (string-match "^[^\n]*\n" gud-marker-acc) + (setq output (concat output (substring gud-marker-acc 0 (match-end 0))) + gud-marker-acc (substring gud-marker-acc (match-end 0)))) + + (setq output (concat output gud-marker-acc) + gud-marker-acc "") + ;; Check our output for a prompt, and existence of a frame. + ;; If t his is true, throw out the debug arrow stuff. + (if (and (string-match "^>> $" output) + gud-last-last-frame) + (progn + (setq overlay-arrow-position nil + gud-last-last-frame nil + gud-overlay-arrow-position nil) + (sit-for 0) + ))) + + (if frame (setq gud-last-frame frame)) + + ;;(message "[%s] [%s]" output gud-marker-acc) + + output)) + +(defun gud-matlab-find-file (f) + "Find file F when debugging frames in MATLAB." + (save-excursion + (let* ((realfname (if (string-match "\\.\\(p\\)$" f) + (progn + (aset f (match-beginning 1) ?m) + f) + f)) + (buf (find-file-noselect realfname))) + (set-buffer buf) + (if (fboundp 'gud-make-debug-menu) + (gud-make-debug-menu)) + buf))) + +(defun matlab-shell-next-matching-input-from-input (n) + "Get the Nth next matching input from for the command line." + (interactive "p") + (matlab-shell-previous-matching-input-from-input (- n))) + +(defun matlab-shell-previous-matching-input-from-input (n) + "Get the Nth previous matching input from for the command line." + (interactive "p") + (if (comint-after-pmark-p) + (if (memq last-command '(matlab-shell-previous-matching-input-from-input + matlab-shell-next-matching-input-from-input)) + ;; This hack keeps the cycling working well. + (let ((last-command 'comint-previous-matching-input-from-input)) + (comint-next-matching-input-from-input (- n))) + ;; first time. + (comint-next-matching-input-from-input (- n))) + + ;; If somewhere else, just move around. + (previous-line n))) + +(defun matlab-shell-delete-backwards-no-prompt (&optional arg) + "Delete one char backwards without destroying the matlab prompt. +Optional argument ARG describes the number of chars to delete." + (interactive "P") + (let ((promptend (save-excursion + (beginning-of-line) + (if (looking-at "K?>> ") + (match-end 0) + (point)))) + (numchars (if (integerp arg) (- arg) -1))) + (if (<= promptend (+ (point) numchars)) + (delete-char numchars) + (error "Beginning of line")))) + +(defun matlab-shell-completion-list (str) + "Get a list of completions from MATLAB. +STR is a substring to complete." + (save-excursion + (let* ((msbn (matlab-shell-buffer-barf-not-running)) + (cmd (concat "matlabMCRprocess = com.mathworks.jmi.MatlabMCR\n" + "matlabMCRprocess.mtFindAllTabCompletions('" + str "'), clear('matlabMCRprocess');")) + (comint-scroll-show-maximum-output nil) + output + (completions nil)) + (set-buffer msbn) + (if (not (matlab-on-prompt-p)) + (error "MATLAB shell must be non-busy to do that")) + (setq output (matlab-shell-collect-command-output cmd)) + ;; Debug + (string-match "ans =" output) + (setq output (substring output (match-end 0))) + ;; Parse the output string. + (while (string-match "'" output) + ;; Hack off the preceeding quote + (setq output (substring output (match-end 0))) + (string-match "'" output) + ;; we are making a completion list, so that is a list of lists. + (setq completions (cons (list (substring output 0 (match-beginning 0))) + completions) + output (substring output (match-end 0)))) + ;; Return them + (nreverse completions)))) + +(defun matlab-shell-which-fcn (fcn) + "Get the location of FCN's M file. +Returns an alist: ( LOCATION . BUILTINFLAG ) +LOCATION is a string indicating where it is, and BUILTINFLAG is +non-nil if FCN is a builtin." + (save-excursion + (let* ((msbn (matlab-shell-buffer-barf-not-running)) + (cmd (concat "which " fcn)) + (comint-scroll-show-maximum-output nil) + output + builtin + ) + (set-buffer msbn) + (if (not (matlab-on-prompt-p)) + (error "MATLAB shell must be non-busy to do that")) + (setq output (matlab-shell-collect-command-output cmd)) + ;; BUILT-IN + (cond + ((string-match "built-in (\\([^)]+\\))" output) + (cons (concat (substring output (match-beginning 1) (match-end 1)) + ".m") + t)) + ;; Error + ((string-match "not found" output) + nil) + ;; JUST AN M FILE + (t + (string-match "$" output) + (cons (substring output 0 (match-beginning 0)) nil)))))) + +(defun matlab-shell-matlabroot () + "Get the location of of this shell's root. +Returns a string path to the root of the executing MATLAB." + (save-excursion + (let* ((msbn (matlab-shell-buffer-barf-not-running)) + (cmd "disp(matlabroot)") + (comint-scroll-show-maximum-output nil) + output + builtin + ) + (set-buffer msbn) + + (if (and (boundp 'matlab-shell-matlabroot-run) + matlab-shell-matlabroot-run) + matlab-shell-matlabroot-run + ;; If we haven't cache'd it, calculate it now. + + (if (not (matlab-on-prompt-p)) + (error "MATLAB shell must be non-busy to do that")) + (setq output (matlab-shell-collect-command-output cmd)) + + (string-match "$" output) + (substring output 0 (match-beginning 0)))))) + + +(defun matlab-shell-tab () + "Send [TAB] to the currently running matlab process and retrieve completion." + (interactive) + (if (not matlab-shell-ask-MATLAB-for-completions) + (call-interactively 'comint-dynamic-complete-filename) + (if (not (matlab-on-prompt-p)) + (error "Completions not available")) + (if nil + ;; For older versions of MATLAB that don't have TAB + ;; completion. + (call-interactively 'comint-dynamic-complete-filename) + ;; Save the old command + (goto-char (point-max)) + (let ((inhibit-field-text-motion t)) + (beginning-of-line)) + (re-search-forward comint-prompt-regexp) + (let* ((lastcmd (buffer-substring (point) (matlab-point-at-eol))) + (tempcmd lastcmd) + (completions nil) + (limitpos nil)) + ;; search for character which limits completion, and limit command to it + (setq limitpos + (if (string-match ".*\\([( /[,;=']\\)" lastcmd) + (1+ (match-beginning 1)) + 0)) + (setq lastcmd (substring lastcmd limitpos)) + ;; Whack the old command so we can insert it back later. + (delete-region (+ (point) limitpos) (matlab-point-at-eol)) + ;; double every single quote + (while (string-match "[^']\\('\\)\\($\\|[^']\\)" tempcmd) + (setq tempcmd (replace-match "''" t t tempcmd 1))) + ;; collect the list + (setq completions (matlab-shell-completion-list tempcmd)) + (goto-char (point-max)) + (if (eq (length completions) 1) + ;; If there is only one, then there is an obvious thing to do. + (progn + (insert (car (car completions))) + ;; kill completions buffer if still visible + (when (get-buffer "*Completions*") + (delete-windows-on "*Completions*"))) + (let ((try (try-completion lastcmd completions))) + ;; Insert in a good completion. + (cond ((or (eq try nil) (eq try t) + (and (stringp try) + (string= try lastcmd))) + (insert lastcmd) + (with-output-to-temp-buffer "*Completions*" + (display-completion-list (mapcar 'car completions) lastcmd))) + ((stringp try) + (insert try) + (when (get-buffer "*Completions*") + (delete-windows-on "*Completions*"))) + (t + (insert lastcmd)))) + ))))) + + +;;; MATLAB mode Shell commands ================================================ + +(defun matlab-show-matlab-shell-buffer () + "Switch to the buffer containing the matlab process." + (interactive) + (let ((msbn (concat "*" matlab-shell-buffer-name "*"))) + (if (get-buffer msbn) + (switch-to-buffer-other-window msbn) + (message "There is not an active MATLAB process.")))) + +(defvar matlab-shell-save-and-go-history '("()") + "Keep track of parameters passed to the MATLAB shell.") + +(defun matlab-shell-add-to-input-history (string) + "Add STRING to the input-ring and run `comint-input-filter-functions' on it. +Similar to `comint-send-input'." + (if (and (funcall comint-input-filter string) + (or (null comint-input-ignoredups) + (not (ring-p comint-input-ring)) + (ring-empty-p comint-input-ring) + (not (string-equal (ring-ref comint-input-ring 0) string)))) + (ring-insert comint-input-ring string)) + (run-hook-with-args 'comint-input-filter-functions + (concat string "\n")) + (if (boundp 'comint-save-input-ring-index);only bound in GNU emacs + (setq comint-save-input-ring-index comint-input-ring-index)) + (setq comint-input-ring-index nil)) + +(defun matlab-shell-save-and-go () + "Save this M file, and evaluate it in a MATLAB shell." + (interactive) + (if (not (eq major-mode 'matlab-mode)) + (error "Save and go is only useful in a MATLAB buffer!")) + (if (not (buffer-file-name (current-buffer))) + (call-interactively 'write-file)) + (let ((fn-name (file-name-sans-extension + (file-name-nondirectory (buffer-file-name)))) + (msbn (concat "*" matlab-shell-buffer-name "*")) + (param "")) + (save-buffer) + ;; Do we need parameters? + (if (save-excursion + (goto-char (point-min)) + (end-of-line) + (forward-sexp -1) + (looking-at "([a-zA-Z]")) + (setq param (read-string "Parameters: " + (car matlab-shell-save-and-go-history) + 'matlab-shell-save-and-go-history))) + (if (matlab-with-emacs-link) + ;; Execute the current file in MATLAB + (matlab-eei-run) + + ;; No buffer? Make it! + (if (not (get-buffer msbn)) (matlab-shell)) + ;; Ok, now fun the function in the matlab shell + (if (get-buffer-window msbn t) + (select-window (get-buffer-window msbn t)) + (switch-to-buffer (concat "*" matlab-shell-buffer-name "*"))) + + (let ((cmd (concat fn-name " " param))) + (matlab-shell-add-to-input-history cmd) + (matlab-shell-send-string (concat cmd "\n")))))) + +(defun matlab-shell-run-region (beg end) + "Run region from BEG to END and display result in MATLAB shell. +This command requires an active MATLAB shell." + (interactive "r") + (if (> beg end) (let (mid) (setq mid beg beg end end mid))) + (let ((command (let ((str (concat (buffer-substring-no-properties beg end) + "\n"))) + (while (string-match "\n\\s-*\n" str) + (setq str (concat (substring str 0 (match-beginning 0)) + "\n" + (substring str (match-end 0))))) + str)) + (msbn nil) + (lastcmd) + (inhibit-field-text-motion t)) + (if (matlab-with-emacs-link) + ;; Run the region w/ Emacs Link + (matlab-eei-eval-region beg end) + + (save-excursion + (setq msbn (matlab-shell-buffer-barf-not-running)) + (set-buffer msbn) + (if (not (matlab-on-prompt-p)) + (error "MATLAB shell must be non-busy to do that")) + ;; Save the old command + (beginning-of-line) + (re-search-forward comint-prompt-regexp) + (setq lastcmd (buffer-substring (point) (matlab-point-at-eol))) + (delete-region (point) (matlab-point-at-eol)) + ;; We are done error checking, run the command. + (matlab-shell-send-string command) + (insert lastcmd)) + (set-buffer msbn) + (goto-char (point-max)) + (display-buffer msbn)) + )) + +(defun matlab-shell-run-cell () + "Run the cell the cursor is in." + (interactive) + (let ((start (save-excursion (forward-page -1) + (if (looking-at "function") + (error "You are not in a cell. Try `matlab-shell-save-and-go' instead")) + (when (matlab-ltype-comm) + ;; Skip over starting comment from the current cell. + (matlab-end-of-command 1) + (end-of-line) + (forward-char 1)) + (point))) + (end (save-excursion (forward-page 1) + (when (matlab-ltype-comm) + (beginning-of-line) + (forward-char -1)) + (point)))) + (matlab-shell-run-region start end))) + +(defun matlab-shell-run-region-or-line () + "Run region from BEG to END and display result in MATLAB shell. +pIf region is not active run the current line. +This command requires an active MATLAB shell." + (interactive) + (if (and transient-mark-mode mark-active) + (matlab-shell-run-region (mark) (point)) + (matlab-shell-run-region (matlab-point-at-bol) (matlab-point-at-eol)))) + + +;;; MATLAB Shell Commands ===================================================== + +(defun matlab-read-word-at-point () + "Get the word closest to point, but do not change position. +Has a preference for looking backward when not directly on a symbol. +Snatched and hacked from dired-x.el" + (let ((word-chars "a-zA-Z0-9_") + (bol (matlab-point-at-bol)) + (eol (matlab-point-at-eol)) + start) + (save-excursion + ;; First see if just past a word. + (if (looking-at (concat "[" word-chars "]")) + nil + (skip-chars-backward (concat "^" word-chars "{}()\[\]") bol) + (if (not (bobp)) (backward-char 1))) + (if (numberp (string-match (concat "[" word-chars "]") + (char-to-string (following-char)))) + (progn + (skip-chars-backward word-chars bol) + (setq start (point)) + (skip-chars-forward word-chars eol)) + (setq start (point))) ; If no found, return empty string + (buffer-substring start (point))))) + +(defun matlab-read-line-at-point () + "Get the line under point, if command line." + (if (eq major-mode 'matlab-shell-mode) + (save-excursion + (let ((inhibit-field-text-motion t)) + (beginning-of-line) + (if (not (looking-at (concat comint-prompt-regexp))) + "" + (search-forward-regexp comint-prompt-regexp) + (buffer-substring (point) (matlab-point-at-eol))))) + (save-excursion + ;; In matlab buffer, find all the text for a command. + ;; so back over until there is no more continuation. + (while (save-excursion (forward-line -1) (matlab-lattr-cont)) + (forward-line -1)) + ;; Go forward till there is no continuation + (beginning-of-line) + (let ((start (point))) + (while (matlab-lattr-cont) (forward-line 1)) + (end-of-line) + (buffer-substring start (point)))))) + +(defun matlab-non-empty-lines-in-string (str) + "Return number of non-empty lines in STR." + (let ((count 0) + (start 0)) + (while (string-match "^.+$" str start) + (setq count (1+ count) + start (match-end 0))) + count)) + +(defun matlab-output-to-temp-buffer (buffer output) + "Print output to temp buffer, or a message if empty string. +BUFFER is the buffer to output to, and OUTPUT is the text to insert." + (let ((lines-found (matlab-non-empty-lines-in-string output))) + (cond ((= lines-found 0) + (message "(MATLAB command completed with no output)")) + ((= lines-found 1) + (string-match "^.+$" output) + (message (substring output (match-beginning 0)(match-end 0)))) + (t (with-output-to-temp-buffer buffer (princ output)) + (save-excursion + (set-buffer buffer) + (matlab-shell-help-mode)))))) + +(defun matlab-shell-run-command (command) + "Run COMMAND and display result in a buffer. +This command requires an active MATLAB shell." + (interactive (list (read-from-minibuffer + "MATLAB command line: " + (cons (matlab-read-line-at-point) 0)))) + (let ((doc (matlab-shell-collect-command-output command))) + (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) + +(defun matlab-shell-describe-variable (variable) + "Get the contents of VARIABLE and display them in a buffer. +This uses the WHOS (MATLAB 5) command to find viable commands. +This command requires an active MATLAB shell." + (interactive (list (read-from-minibuffer + "MATLAB variable: " + (cons (matlab-read-word-at-point) 0)))) + (let ((doc (matlab-shell-collect-command-output (concat "whos " variable)))) + (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) + +(defun matlab-shell-describe-command (command) + "Describe COMMAND textually by fetching it's doc from the MATLAB shell. +This uses the lookfor command to find viable commands. +This command requires an active MATLAB shell." + (interactive + (let ((fn (matlab-function-called-at-point)) + val) + (setq val (read-string (if fn + (format "Describe function (default %s): " fn) + "Describe function: "))) + (if (string= val "") (list fn) (list val)))) + (let ((doc (matlab-shell-collect-command-output (concat "help " command)))) + (matlab-output-to-temp-buffer "*MATLAB Help*" doc))) + +(defun matlab-shell-apropos (matlabregex) + "Look for any active commands in MATLAB matching MATLABREGEX. +This uses the lookfor command to find viable commands." + (interactive (list (read-from-minibuffer + "MATLAB command subexpression: " + (cons (matlab-read-word-at-point) 0)))) + (let ((ap (matlab-shell-collect-command-output + (concat "lookfor " matlabregex)))) + (matlab-output-to-temp-buffer "*MATLAB Apropos*" ap))) + +(defun matlab-on-prompt-p () + "Return t if we MATLAB can accept input." + (save-excursion + (let ((inhibit-field-text-motion t)) + (goto-char (point-max)) + (beginning-of-line) + (looking-at comint-prompt-regexp)))) + +(defun matlab-on-empty-prompt-p () + "Return t if we MATLAB is on an empty prompt." + (save-excursion + (let ((inhibit-field-text-motion t)) + (goto-char (point-max)) + (beginning-of-line) + (looking-at (concat comint-prompt-regexp "\\s-*$"))))) + +(defun matlab-shell-buffer-barf-not-running () + "Return a running MATLAB buffer iff it is currently active." + (or (matlab-shell-active-p) + (error "You need to run the command `matlab-shell' to do that!"))) + +(defun matlab-shell-collect-command-output (command) + "If there is a MATLAB shell, run the MATLAB COMMAND and return it's output. +It's output is returned as a string with no face properties. The text output +of the command is removed from the MATLAB buffer so there will be no +indication that it ran." + (let ((msbn (matlab-shell-buffer-barf-not-running)) + (pos nil) + (str nil) + (lastcmd) + (inhibit-field-text-motion t)) + (save-excursion + (set-buffer msbn) + (if (not (matlab-on-prompt-p)) + (error "MATLAB shell must be non-busy to do that")) + ;; Save the old command + (goto-char (point-max)) + (beginning-of-line) + (re-search-forward comint-prompt-regexp) + (setq lastcmd (buffer-substring (point) (matlab-point-at-eol))) + (delete-region (point) (matlab-point-at-eol)) + ;; We are done error checking, run the command. + (setq pos (point)) + (comint-send-string (get-buffer-process (current-buffer)) + (concat command "\n")) + ;;(message "MATLAB ... Executing command.") + (goto-char (point-max)) + (while (or (>= (+ pos (string-width command)) (point)) (not (matlab-on-empty-prompt-p))) + (accept-process-output (get-buffer-process (current-buffer))) + (goto-char (point-max)) + ;;(message "MATLAB reading...") + ) + ;;(message "MATLAB reading...done") + (save-excursion + (goto-char pos) + (beginning-of-line) + (setq str (buffer-substring-no-properties (save-excursion + (goto-char pos) + (beginning-of-line) + (forward-line 1) + (point)) + (save-excursion + (goto-char (point-max)) + (beginning-of-line) + (point)))) + (delete-region pos (point-max))) + (insert lastcmd)) + str)) + +(defun matlab-shell-send-string (string) + "Send STRING to the currently running matlab process." + (if (not matlab-shell-echoes) + (let ((proc (get-buffer-process (current-buffer)))) + (goto-char (point-max)) + (insert string) + (set-marker (process-mark proc) (point)))) + (comint-send-string (get-buffer-process (current-buffer)) string)) + +(defun matlab-url-at (p) + "Return the matlab-url overlay at P, or nil." + (let ((url nil) (o (matlab-overlays-at p))) + (while (and o (not url)) + (setq url (matlab-overlay-get (car o) 'matlab-url) + o (cdr o))) + url)) + +(defun matlab-url-stack-top-at (p) + "Return the matlab-url overlay at P, or nil." + (let ((url nil) (o (matlab-overlays-at p))) + (while (and o (not url)) + (setq url (or (matlab-overlay-get (car o) 'first-in-error-stack) + (matlab-overlay-get (car o) 'matlab-url)) + o (cdr o))) + url)) + +(defun matlab-shell-previous-matlab-url (&optional stacktop) + "Find a previous occurrence of an overlay with a MATLAB URL. +If STACKTOP is non-nil, then also get the top of some stack, which didn't +show up in reverse order." + (save-excursion + (let ((url nil) (o nil) (p (point))) + (while (and (not url) + (setq p (matlab-previous-overlay-change p)) + (not (eq p (point-min)))) + (setq url + (if stacktop + (matlab-url-stack-top-at p) + (matlab-url-at p)))) + url))) + +(defun matlab-find-other-window-file-line-column (ef el ec &optional debug) + "Find file EF in other window and to go line EL and 1-basec column EC. +If DEBUG is non-nil, then setup GUD debugging features." + (cond ((file-exists-p ef) + nil);; keep ef the same + ((file-exists-p (concat ef ".m")) + (setq ef (concat ef ".m"))) ;; Displayed w/out .m? + ((string-match ">" ef) + (setq ef (concat (substring ef 0 (match-beginning 0)) ".m"))) + ) + (find-file-other-window ef) + (goto-line (string-to-number el)) + (when debug + (setq gud-last-frame (cons (buffer-file-name) (string-to-number el))) + (gud-display-frame)) + (setq ec (string-to-number ec)) + (if (> ec 0) (forward-char (1- ec)))) + +(defun matlab-find-other-window-via-url (url &optional debug) + "Find other window using matlab URL and optionally set DEBUG cursor." + (cond ((string-match "^error:\\(.*\\),\\([0-9]+\\),\\([0-9]+\\)$" url) + (let ((ef (substring url (match-beginning 1) (match-end 1))) + (el (substring url (match-beginning 2) (match-end 2))) + (ec (substring url (match-beginning 3) (match-end 3)))) + (matlab-find-other-window-file-line-column ef el ec debug))) + ((string-match "opentoline('\\([^']+\\)',\\([0-9]+\\),\\([0-9]+\\))" url) + (let ((ef (substring url (match-beginning 1) (match-end 1))) + (el (substring url (match-beginning 2) (match-end 2))) + (ec (substring url (match-beginning 3) (match-end 3)))) + (matlab-find-other-window-file-line-column ef el ec debug))) + ((string-match "^matlab: *\\(.*\\)$" url) + (process-send-string + (get-buffer-process gud-comint-buffer) + (concat (substring url (match-beginning 1) (match-end 1)) "\n"))))) + +(defun matlab-shell-last-error () + "In the MATLAB interactive buffer, find the last MATLAB error, and go there. +To reference old errors, put the cursor just after the error text." + (interactive) + (catch 'done + (let ((url (matlab-shell-previous-matlab-url t))) + (if url + (progn (matlab-find-other-window-via-url url) (throw 'done nil)) + (save-excursion + (end-of-line) ;; In case we are before the linenumber 1998/06/05 16:54sk + (if (not (re-search-backward gud-matlab-error-regexp nil t)) + (error "No errors found!")) + (let ((ef (buffer-substring-no-properties + (match-beginning 2) (match-end 2))) + (el (buffer-substring-no-properties + (match-beginning 3) (match-end 3)))) + (matlab-find-other-window-file-line-column ef el 0))))))) + +(defun matlab-shell-html-click (e) + "Go to the error at the location of event E." + (interactive "e") + (mouse-set-point e) + (let ((url (matlab-url-at (point)))) + (if url (matlab-find-other-window-via-url url)))) + +(defun matlab-shell-dbstop-error () + "Stop on errors." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) + "dbstop if error\n")) + +(defun matlab-shell-dbclear-error () + "Don't stop on errors." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) + "dbclear if error\n")) + +(defun matlab-shell-demos () + "MATLAB demos." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) "demo\n")) + +(defun matlab-shell-close-figures () + "Close any open figures." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) "close all\n")) + +(defun matlab-shell-close-current-figure () + "Close current figure." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) "delete(gcf)\n")) + +(defun matlab-shell-exit () + "Exit MATLAB shell." + (interactive) + (comint-send-string (get-buffer-process (current-buffer)) "exit\n") + (kill-buffer nil)) + + +;;; matlab-shell based Topic Browser and Help ================================= + +(defcustom matlab-shell-topic-mode-hook nil + "*MATLAB shell topic hook." + :group 'matlab-shell + :type 'hook) + +(defvar matlab-shell-topic-current-topic nil + "The currently viewed topic in a MATLAB shell topic buffer.") + +(defun matlab-shell-topic-browser () + "Create a topic browser by querying an active MATLAB shell using HELP. +Maintain state in our topic browser buffer." + (interactive) + ;; Reset topic browser if it doesn't exist. + (if (not (get-buffer "*MATLAB Topic*")) + (setq matlab-shell-topic-current-topic nil)) + (let ((b (get-buffer-create "*MATLAB Topic*"))) + (switch-to-buffer b) + (if (string= matlab-shell-topic-current-topic "") + nil + (matlab-shell-topic-mode) + (matlab-shell-topic-browser-create-contents "")))) + +(defvar matlab-shell-topic-mouse-face-keywords + '(;; These are subtopic fields... + ("^\\(\\w+/\\w+\\)[ \t]+-" 1 font-lock-reference-face) + ;; These are functions... + ("^[ \t]+\\(\\w+\\)[ \t]+-" 1 font-lock-function-name-face) + ;; Here is a See Also line... + ("[ \t]+See also " + ("\\(\\w+\\)\\([,.]\\| and\\|$\\) *" nil nil (1 font-lock-reference-face)))) + "These are keywords we also want to put mouse-faces on.") + +(defvar matlab-shell-topic-font-lock-keywords + (append matlab-shell-topic-mouse-face-keywords + '(("^[^:\n]+:$" 0 font-lock-keyword-face) + ;; These are subheadings... + ("^[ \t]+\\([^.\n]+[a-zA-Z.]\\)$" 1 'underline) + )) + "Keywords useful for highlighting a MATLAB TOPIC buffer.") + +(defvar matlab-shell-help-font-lock-keywords + (append matlab-shell-topic-mouse-face-keywords + '(;; Function call examples + ("[ \t]\\([A-Z]+\\)\\s-*=\\s-*\\([A-Z]+[0-9]*\\)(" + (1 font-lock-variable-name-face) + (2 font-lock-function-name-face)) + ("[ \t]\\([A-Z]+[0-9]*\\)(" + (1 font-lock-function-name-face)) + ;; Parameters: Not very accurate, unfortunately. + ("[ \t]\\([A-Z]+[0-9]*\\)(" + ("'?\\(\\w+\\)'?\\([,)]\\) *" nil nil + (1 font-lock-variable-name-face)) + ) + ;; Reference uppercase words + ("\\<\\([A-Z]+[0-9]*\\)\\>" 1 font-lock-reference-face))) + "Keywords for regular help buffers.") + +;; View-major-mode is an emacs20 thing. This gives us a small compatibility +;; layer. +(if (not (fboundp 'view-major-mode)) (defalias 'view-major-mode 'view-mode)) + +(define-derived-mode matlab-shell-help-mode + view-major-mode "M-Help" + "Major mode for viewing MATLAB help text. +Entry to this mode runs the normal hook `matlab-shell-help-mode-hook'. + +Commands: +\\{matlab-shell-help-mode-map}" + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '((matlab-shell-help-font-lock-keywords) + t nil ((?_ . "w")))) + ;; This makes sure that we really enter font lock since + ;; kill-all-local-variables is not used by old view-mode. + (and (boundp 'global-font-lock-mode) global-font-lock-mode + (not font-lock-mode) (font-lock-mode 1)) + (easy-menu-add matlab-shell-help-mode-menu matlab-shell-help-mode-map) + (matlab-shell-topic-mouse-highlight-subtopics) + ) + +(define-key matlab-shell-help-mode-map [return] 'matlab-shell-topic-choose) +(define-key matlab-shell-help-mode-map "t" 'matlab-shell-topic-browser) +(define-key matlab-shell-help-mode-map "q" 'bury-buffer) +(define-key matlab-shell-help-mode-map + [(control h) (control m)] matlab-help-map) +(if (string-match "XEmacs" emacs-version) + (define-key matlab-shell-help-mode-map [button2] 'matlab-shell-topic-click) + (define-key matlab-shell-help-mode-map [mouse-2] 'matlab-shell-topic-click)) + +(easy-menu-define + matlab-shell-help-mode-menu matlab-shell-help-mode-map + "MATLAB shell topic menu" + '("MATLAB Help" + ["Describe This Command" matlab-shell-topic-choose t] + "----" + ["Describe Command" matlab-shell-describe-command t] + ["Describe Variable" matlab-shell-describe-variable t] + ["Command Apropos" matlab-shell-apropos t] + ["Topic Browser" matlab-shell-topic-browser t] + "----" + ["Exit" bury-buffer t])) + +(define-derived-mode matlab-shell-topic-mode + matlab-shell-help-mode "M-Topic" + "Major mode for browsing MATLAB HELP topics. +The output of the MATLAB command HELP with no parameters creates a listing +of known help topics at a given installation. This mode parses that listing +and allows selecting a topic and getting more help for it. +Entry to this mode runs the normal hook `matlab-shell-topic-mode-hook'. + +Commands: +\\{matlab-shell-topic-mode-map}" + (setq font-lock-defaults '((matlab-shell-topic-font-lock-keywords) + t t ((?_ . "w")))) + (if (string-match "XEmacs" emacs-version) + (setq mode-motion-hook 'matlab-shell-topic-highlight-line)) + (easy-menu-add matlab-shell-topic-mode-menu matlab-shell-topic-mode-map) + ) + +(easy-menu-define + matlab-shell-topic-mode-menu matlab-shell-topic-mode-map + "MATLAB shell topic menu" + '("MATLAB Topic" + ["Select This Topic" matlab-shell-topic-choose t] + ["Top Level Topics" matlab-shell-topic-browser t] + "----" + ["Exit" bury-buffer t])) + +(defun matlab-shell-topic-browser-create-contents (subtopic) + "Fill in a topic browser with the output from SUBTOPIC." + (toggle-read-only -1) + (erase-buffer) + (insert (matlab-shell-collect-command-output (concat "help " subtopic))) + (goto-char (point-min)) + (forward-line 1) + (delete-region (point-min) (point)) + (setq matlab-shell-topic-current-topic subtopic) + (if (not (string-match "XEmacs" emacs-version)) + (matlab-shell-topic-mouse-highlight-subtopics)) + (toggle-read-only 1) + ) + +(defun matlab-shell-topic-click (e) + "Click on an item in a MATLAB topic buffer we want more information on. +Must be bound to event E." + (interactive "e") + (mouse-set-point e) + (matlab-shell-topic-choose)) + +(defun matlab-shell-topic-choose () + "Choose the topic to expand on that is under the cursor. +This can fill the topic buffer with new information. If the topic is a +command, use `matlab-shell-describe-command' instead of changing the topic +buffer." + (interactive) + (let ((topic nil) (fun nil) (p (point))) + (save-excursion + (beginning-of-line) + (if (looking-at "^\\w+/\\(\\w+\\)[ \t]+-") + (setq topic (match-string 1)) + (if (looking-at "^[ \t]+\\(\\(\\w\\|_\\)+\\)[ \t]+-") + (setq fun (match-string 1)) + (if (and (not (looking-at "^[ \t]+See also")) + (not (save-excursion (forward-char -2) + (looking-at ",$")))) + (error "You did not click on a subtopic, function or reference") + (goto-char p) + (forward-word -1) + (if (not (looking-at "\\(\\(\\w\\|_\\)+\\)\\([.,]\\| and\\|\n\\)")) + (error "You must click on a reference") + (setq topic (match-string 1))))))) + (message "Opening item %s..." (or topic fun)) + (if topic + (matlab-shell-topic-browser-create-contents (downcase topic)) + (matlab-shell-describe-command fun)) + )) + +(defun matlab-shell-topic-mouse-highlight-subtopics () + "Put a `mouse-face' on all clickable targets in this buffer." + (save-excursion + (let ((el matlab-shell-topic-mouse-face-keywords)) + (while el + (goto-char (point-min)) + (while (re-search-forward (car (car el)) nil t) + (let ((cd (car (cdr (car el))))) + (if (numberp cd) + (put-text-property (match-beginning cd) (match-end cd) + 'mouse-face 'highlight) + (while (re-search-forward (car cd) nil t) + (put-text-property (match-beginning (car (nth 3 cd))) + (match-end (car (nth 3 cd))) + 'mouse-face 'highlight))))) + (setq el (cdr el)))))) + +(defun matlab-shell-topic-highlight-line (event) + "A value of `mode-motion-hook' which will highlight topics under the mouse. +EVENT is the user mouse event." + ;; XEMACS only function + (let* ((buffer (event-buffer event)) + (point (and buffer (event-point event)))) + (if (and buffer (not (eq buffer mouse-grabbed-buffer))) + (save-excursion + (save-window-excursion + (set-buffer buffer) + (mode-motion-ensure-extent-ok event) + (if (not point) + (detach-extent mode-motion-extent) + (goto-char point) + (end-of-line) + (setq point (point)) + (beginning-of-line) + (if (or (looking-at "^\\w+/\\(\\w+\\)[ \t]+-") + (looking-at "^[ \t]+\\(\\(\\w\\|_\\)+\\)[ \t]+-")) + (set-extent-endpoints mode-motion-extent (point) point) + (detach-extent mode-motion-extent)))))))) + + +;;; M File path stuff ========================================================= + +(defun matlab-mode-determine-mfile-path () + "Create the path in `matlab-mode-install-path'." + (let ((path (file-name-directory matlab-shell-command))) + ;; if we don't have a path, find the MATLAB executable on our path. + (if (not path) + (let ((pl exec-path)) + (while (and pl (not path)) + (if (and (file-exists-p (concat (car pl) "/" matlab-shell-command)) + (not (car (file-attributes (concat (car pl) "/" + matlab-shell-command))))) + (setq path (car pl))) + (setq pl (cdr pl))))) + (if (not path) + nil + ;; When we find the path, we need to massage it to identify where + ;; the M files are that we need for our completion lists. + (if (string-match "/bin$" path) + (setq path (substring path 0 (match-beginning 0)))) + ;; Everything stems from toolbox (I think) + (setq path (concat path "/toolbox/"))) + path)) + +(defcustom matlab-mode-install-path (list (matlab-mode-determine-mfile-path)) + "Base path pointing to the locations of all the m files used by matlab. +All directories under each element of `matlab-mode-install-path' are +checked, so only top level toolbox directories need be added. +Paths should be added in the order in which they should be searched." + :group 'matlab-shell + :type '(repeat (string :tag "Path: "))) + +(defun matlab-find-file-under-path (path filename) + "Return the pathname or nil of PATH under FILENAME." + (if (file-exists-p (concat path filename)) + (concat path filename) + (let ((dirs (if (file-directory-p path) + ;; Not checking as a directory first fails on XEmacs + ;; Stelios Kyriacou <kyriacou@cbmv.jhu.edu> + (directory-files path t nil t))) + (found nil)) + (while (and dirs (not found)) + (if (and (car (file-attributes (car dirs))) + ;; require directory readable + (file-readable-p (car dirs)) + ;; don't redo our path names + (not (string-match "/\\.\\.?$" (car dirs))) + ;; don't find files in object directories. + (not (string-match "@" (car dirs)))) + (setq found + (matlab-find-file-under-path (concat (car dirs) "/") + filename))) + (setq dirs (cdr dirs))) + found))) + +(defun matlab-find-file-on-path (filename) + "Find FILENAME on the current MATLAB path. +The MATLAB path is determined by `matlab-mode-install-path' and the +current directory. You must add user-installed paths into +`matlab-mode-install-path' if you would like to have them included." + (interactive + (list + (let ((default (matlab-read-word-at-point))) + (if default + (let ((s (read-string (concat "File (default " default "): ")))) + (if (string= s "") default s)) + (read-string "File: "))))) + (if (string= filename "") + (error "You must specify an M file")) + (if (not (string-match "\\.m$" filename)) + (setq filename (concat filename ".m"))) + (let ((fname nil) + (dirs matlab-mode-install-path)) + (if (file-exists-p (concat default-directory filename)) + (setq fname (concat default-directory filename))) + (while (and (not fname) dirs) + (if (stringp (car dirs)) + (progn + (message "Searching for %s in %s" filename (car dirs)) + (setq fname (matlab-find-file-under-path (car dirs) filename)))) + (setq dirs (cdr dirs))) + (if fname (find-file fname) + (error "File %s not found on any known paths. \ +Check `matlab-mode-install-path'" filename)))) + +(defun matlab-find-file-click (e) + "Find the file clicked on with event E on the current path." + (interactive "e") + (mouse-set-point e) + (let ((f (matlab-read-word-at-point))) + (if (not f) (error "To find an M file, click on a word")) + (matlab-find-file-on-path f))) + + +;;; matlab-mode debugging ===================================================== + +(defun matlab-show-line-info () + "Display type and attributes of current line. Used in debugging." + (interactive) + (let ((msg "line-info:") + (indent (matlab-calculate-indentation (current-indentation))) + (nexti (matlab-next-line-indentation))) + (setq msg (concat msg + " Line type: " (symbol-name (car indent)) + " This Line: " (int-to-string (nth 1 indent)) + " Next Line: " (int-to-string nexti))) + (if (matlab-lattr-cont) + (setq msg (concat msg " w/cont"))) + (if (matlab-lattr-comm) + (setq msg (concat msg " w/comm"))) + (message msg))) + +(provide 'matlab) + +;;; matlab.el ends here
new file mode 100644 --- /dev/null +++ b/plugins/python-mode.el @@ -0,0 +1,17308 @@ +;; python-mode.el --- Towards an Python-IDE in Emacs + +;; Maintainer: Andreas Roehler <andreas.roehler@online.de> +;; Keywords: languages, processes, python, oop + +;; Copyright (C) 1992,1993,1994 Tim Peters + +;; Author: 2003-2011 https://launchpad.net/python-mode +;; 1995-2002 Barry A. Warsaw +;; 1992-1994 Tim Peters +;; Maintainer: python-mode@python.org +;; Created: Feb 1992 +;; Keywords: python languages oop + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; commands-python-mode.org in directory doc reports +;; available commands, also a menu is provided + +;; as for `py-add-abbrev': +;; Similar to `add-mode-abbrev', but uses +;; `py-partial-expression' before point for expansion to +;; store, not `word'. Also provides a proposal for new +;; abbrevs. + +;; Proposal for an abbrev is composed from the downcased +;; initials of expansion - provided they are of char-class +;; [:alpha:] +;; +;; For example code below would be recognised as a +;; `py-expression' composed by three +;; py-partial-expressions. +;; +;; OrderedDict.popitem(last=True) +;; +;; Putting the curser at the EOL, M-3 M-x py-add-abbrev +;; +;; would prompt "op" for an abbrev to store, as first +;; `py-partial-expression' beginns with a "(", which is +;; not taken as proposal. + +;;; Code + +(require 'comint) +(require 'custom) +(eval-when-compile (require 'cl)) +(require 'compile) +(require 'ansi-color) +(require 'cc-cmds) +(require 'shell) +(require 'rx) +(require 'flymake) +(require 'imenu) + +(defgroup python-mode nil + "Support for the Python programming language, <http://www.python.org/>" + :group 'languages + :prefix "py-") + +(defconst py-version "6.0.10") + +(defvar python-local-version nil + "Used internally. ") +(make-variable-buffer-local 'python-local-version) + +(defvar py-local-command nil + "Returns locally used executable-name. ") +(make-variable-buffer-local 'py-local-command) + +(defvar py-local-versioned-command nil + "Returns locally used executable-name including its version. ") +(make-variable-buffer-local 'py-local-versioned-command) + +(defcustom python-mode-modeline-display "Py" + "String to display in Emacs modeline " + + :type 'string + :group 'python-mode) + +;;; User definable variables +(defcustom py-indent-offset 4 + "*Amount of offset per level of indentation. +`\\[py-guess-indent-offset]' can usually guess a good value when +you're editing someone else's Python code." + :type 'integer + :group 'python-mode) +(make-variable-buffer-local 'py-indent-offset) + +(defcustom pdb-path '/usr/lib/python2.7/pdb.py + "Where to find pdb.py. Edit this according to your system. + +If you ignore the location `M-x py-guess-pdb-path' might display it. +" + :type 'variable + :group 'python-mode) + +(defcustom py-verbose-p nil + "If indenting functions should report reached indent level. + +Default is nil. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-prepare-autopair-mode-p t + "If autopair-mode hook should be loaded. Default is `t'. " + :type 'boolean + :group 'python-mode) + +(defcustom py-fontify-shell-buffer-p nil + "If code in Python shell should be highlighted as in script buffer. + +Default is nil. + +If `t', related vars like `comment-start' will be set too. +Seems convenient when playing with stuff in IPython shell +Might not be TRT when a lot of output arrives " + + :type 'boolean + :group 'python-mode) + +(defcustom py-modeline-display-full-path-p nil + "If the full PATH/TO/PYTHON should be displayed in shell modeline. + +Default is nil. Note: when `py-shell-name' is specified with path, it's shown as an acronym in buffer-name already. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-modeline-acronym-display-home-p nil + "If the modeline acronym should contain chars indicating the home-directory. + +Default is nil " + :type 'boolean + :group 'python-mode) + +(defun py-guess-pdb-path () + "If py-pdb-path isn't set, find location of pdb.py. " + (interactive) + (let ((ele (split-string (shell-command-to-string "whereis python"))) + erg) + (while (or (not erg)(string= "" erg)) + (when (and (string-match "^/" (car ele)) (not (string-match "/man" (car ele)))) + (setq erg (shell-command-to-string (concat "find " (car ele) " -type f -name \"pdb.py\"")))) + (setq ele (cdr ele))) + (if erg + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + (when (interactive-p) (message "%s" "pdb.py not found, please customize `pdb-path'"))) + (concat "'" erg))) + +(defcustom py-install-directory "" + "Directory where python-mode.el and it's subdirectories should be installed. Needed for completion and other environment stuff only. " + :type 'string + :group 'python-mode) + +(defcustom py-guess-py-install-directory-p t + "If in cases, `py-install-directory' isn't set, `py-set-load-path'should guess it from `buffer-file-name'. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-report-position-p nil + "If functions moving point like `py-forward-into-nomenclature' should report reached position. + +Default is nil. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-extensions "py-extensions.el" + "File where extensions to python-mode.el should be installed. Used by virtualenv support. " + + :type 'string + :group 'python-mode) + +(defcustom py-hide-show-minor-mode-p nil + "If hide-show minor-mode should be on, default is nil. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-org-cycle-p nil + "When non-nil, command `org-cycle' is available at shift-TAB, <backtab> + +Default is nil. " + + :type 'boolean + :group 'python-mode) + +(defcustom ipython-complete-use-separate-shell-p nil + + "If `ipython-complete' should use a separate shell. Thus prompt-counter is not incremented by completion. " + :type 'boolean :group 'python-mode) + +(defcustom py-outline-minor-mode-p t + "If outline minor-mode should be on, default is `t'. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-outline-mode-keywords + '("class" "def" "elif" "else" "except" + "for" "if" "while" "finally" "try" + "with") + "Keywords composing visible heads. " + :type '(repeat string) + :group 'python-mode) + +(defcustom py-start-run-py-shell t + "If `python-mode' should start a python-shell, `py-shell'. Default is `t'. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-start-run-ipython-shell t + "If `python-mode' should start an ipython-shell. Default is `t'. + +A running ipython-shell presently is needed by `ipython-complete', otherwise first try will fail. " + + :type 'boolean + :group 'python-mode) + +;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +(defcustom py-close-provides-newline t + "If a newline is inserted, when line after block isn't empty. Default is non-nil. " + :type 'boolean + :group 'python-mode) +(make-variable-buffer-local 'py-close-provides-newline) + +(defcustom py-dedent-keep-relative-column t + "If point should follow dedent or kind of electric move to end of line. Default is t - keep relative position. " + :type 'boolean + :group 'python-mode) + +(defcustom py-indent-honors-multiline-listing nil + "If `t', indents to 1+ column of opening delimiter. If `nil', indent adds one level to the beginning of statement. Default is `nil'. " + :type 'boolean + :group 'python-mode) + +(defcustom py-indent-honors-inline-comment nil + "If non-nil, indents to column of inlined comment start. +Default is nil. " + :type 'boolean + :group 'python-mode) + +(defcustom py-closing-list-dedents-bos nil + "If non-nil, closing parentesis dedents onto column of statement, otherwise keeps additional `py-indent-offset', default is nil " + :type 'boolean + :group 'python-mode) + +(defcustom py-electric-colon-active-p nil + "`py-electric-colon' feature. Default is `nil'. See lp:837065 for discussions. " + :type 'boolean + :group 'python-mode) + +(defcustom py-electric-comment-p t + "If \"#\" should call `py-electric-comment'. Default is `t'. " + :type 'boolean + :group 'python-mode) + +(defcustom py-electric-comment-add-space-p nil + "If py-electric-comment should add a space. Default is `nil'. " + :type 'boolean + :group 'python-mode) + +(defcustom py-mark-decorators nil + "If py-mark-def-or-class functions should mark decorators too. Default is `nil'. " + :type 'boolean + :group 'python-mode) + +(defcustom py-tab-indent t + "*Non-nil means TAB in Python mode calls `py-indent-line'." + :type 'boolean + :group 'python-mode) + +(defcustom py-complete-function 'nil + "When set, enforces function todo completion, default is nil. + +Normally python-mode, resp. inferior-python-mode know best which functin to use. " + :type '(choice + (const :tag "default" nil) + (const :tag "py-completion-at-point" py-completion-at-point) + (const :tag "Pymacs based py-complete" py-complete) + (const :tag "py-shell-complete" py-shell-complete) + (const :tag "IPython's ipython-complete" ipython-complete) + ) + :group 'python-mode) + +(defcustom ipython-complete-function 'ipython-complete + "Function used for completion in IPython shell buffers. + +Minor bug: `ipython-complete' raises the prompt counter when completion done + +Richard Everson commented: + + I don't know how to stop IPython from incrementing the prompt + counter, but using py-completion-at-point just hangs emacs for + me. If I start with a new IPython shell, then + + In [1]: import sys + + In [2]: sys.pa + + then M-x py-completion-at-point, hoping to complete to sys.path, Emacs + hangs. Escaping out of it shows that the \*Python\* buffer has the + contents: + + >>> Traceback (most recent call last): + File \"<stdin>\", line 1, in <module> + NameError: name 'nil' is not defined + >>> = + [ ... ] + + On the other hand, IPython's interaction and completion itself is pretty + impressive (for versions greater than 0.10 at least): it inserts the + correct indentation for for, if, etc and it will show completions even + within a loop. Here's an example from a terminal shell: + + In [1]: + + In [1]: for i in range(3): + ...: print i, sys.p<------------ Pressed tab here; indentation inser= + ted automatically + sys.path sys.path_importer_cache sys.prefix + sys.path_hooks sys.platform sys.py3kwarning + ...: print i, sys.path<------------ Pressed tab again + sys.path sys.path_hooks sys.path_importer_cache +" + :type '(choice (const :tag "py-completion-at-point" py-completion-at-point) + (const :tag "py-shell-complete" py-shell-complete) + (const :tag "Pymacs based py-complete" py-complete) + (const :tag "IPython's ipython-complete" ipython-complete)) + :group 'python-mode) +(make-variable-buffer-local 'ipython-complete-function) + +(defvar py-local-complete-function nil + "Set by python-mode-hook resp. to environment. + +`py-complete-function', when set, overrides it. ") +(make-variable-buffer-local 'py-local-complete-function) + +(defcustom py-encoding-string " # -*- coding: utf-8 -*-" + "Default string specifying encoding of a Python file. " + :type 'string + :group 'python-mode) + +(defvar py-encoding-string-re "^[ \t]*#[ \t]*-\\*-[ \t]*coding:.+-\\*-" + "Matches encoding string of a Python file. ") + +(defvar symbol-definition-start-re) +(setq symbol-definition-start-re "^[ \t]*(\\(defun\\|defvar\\|defcustom\\)") +(defcustom py-shebang-startstring "#! /bin/env" + "Detecting the shell in head of file. " + :type 'string + :group 'python-mode) + +(defvar py-shebang-regexp "#![ \t]?\\([^ \t\n]+\\)[ \t]?\\([biptj]+ython[^ \t\n]*\\)" + "Detecting the shell in head of file. ") + +(defcustom py-python-command-args '("-i") + "*List of string arguments to be used when starting a Python shell." + :type '(repeat string) + :group 'python-mode) +(make-variable-buffer-local 'py-python-command-args) + +(set-default 'py-python-command-args '("-i")) + +(make-obsolete-variable 'py-jpython-command-args 'py-jython-command-args nil) +(defcustom py-jython-command-args '("-i") + "*List of string arguments to be used when starting a Jython shell." + :type '(repeat string) + :group 'python-mode + :tag "Jython Command Args") + +(defcustom py-cleanup-temporary t + "If temporary buffers and files used by functions executing region should be deleted afterwards. " + :type 'boolean + :group 'python-mode) + +(defcustom py-lhs-inbound-indent 1 + "When line starts a multiline-assignment: How many colums indent should be more than opening bracket, brace or parenthesis. " + :type 'integer + :group 'python-mode) +(make-variable-buffer-local 'py-lhs-inbound-indent) + +(defcustom py-rhs-inbound-indent 1 + "When inside a multiline-assignment: How many colums indent should be more than opening bracket, brace or parenthesis. " + :type 'integer + :group 'python-mode) +(make-variable-buffer-local 'py-rhs-inbound-indent) + +(defcustom py-continuation-offset 2 + "*Additional amount of offset to give for some continuation lines. +Continuation lines are those that immediately follow a backslash +terminated line. " + :type 'integer + :group 'python-mode) + +(defcustom py-indent-tabs-mode nil + "Python-mode starts `indent-tabs-mode' with the value specified here, default is nil. " + :type 'boolean + :group 'python-mode) + +(defcustom py-smart-indentation t + "*Should `python-mode' try to automagically set some indentation variables? +When this variable is non-nil, two things happen when a buffer is set +to `python-mode': + + 1. `py-indent-offset' is guessed from existing code in the buffer. + Only guessed values between 2 and 8 are considered. If a valid + guess can't be made (perhaps because you are visiting a new + file), then the value in `py-indent-offset' is used. + + 2. `indent-tabs-mode' is turned off if `py-indent-offset' does not + equal `tab-width' (`indent-tabs-mode' is never turned on by + Python mode). This means that for newly written code, tabs are + only inserted in indentation if one tab is one indentation + level, otherwise only spaces are used. + +Note that both these settings occur *after* `python-mode-hook' is run, +so if you want to defeat the automagic configuration, you must also +set `py-smart-indentation' to nil in your `python-mode-hook'." + :type 'boolean + :group 'python-mode) +(make-variable-buffer-local 'py-smart-indentation) + +(defcustom py-align-multiline-strings-p t + "*Flag describing how multi-line triple quoted strings are aligned. +When this flag is non-nil, continuation lines are lined up under the +preceding line's indentation. When this flag is nil, continuation +lines are aligned to column zero." + :type '(choice (const :tag "Align under preceding line" t) + (const :tag "Align to column zero" nil)) + :group 'python-mode) + +(defcustom py-block-comment-prefix "##" + "*String used by \\[comment-region] to comment out a block of code. +This should follow the convention for non-indenting comment lines so +that the indentation commands won't get confused (i.e., the string +should be of the form `#x...' where `x' is not a blank or a tab, and +`...' is arbitrary). However, this string should not end in whitespace." + :type 'string + :group 'python-mode) + +(defcustom py-indent-comments t + "When t, comment lines are indented. " + :type 'boolean + :group 'python-mode) + +(defvar py-separator-char 47 + "Values set by defcustom only will not be seen in batch-mode. ") + +(defcustom py-separator-char 47 + "The character, which separates the system file-path components. + +Precedes guessing when not empty, returned by function `py-separator-char'. " + :type 'character + :group 'python-mode) + +(defcustom py-custom-temp-directory "" + "If set, will take precedence over guessed values from `py-temp-directory'. Default is the empty string. + +When set, make sure the directory exists. " + :type 'string + :group 'python-mode) + +(defvar py-temp-directory + (let ((ok '(lambda (x) + (and x + (setq x (expand-file-name x)) ; always true + (file-directory-p x) + (file-writable-p x) + x))) + erg) + (or + (and (not (string= "" py-custom-temp-directory)) + (if (funcall ok py-custom-temp-directory) + (setq erg (expand-file-name py-custom-temp-directory)) + (if (file-directory-p (expand-file-name py-custom-temp-directory)) + (error "py-custom-temp-directory set but not writable") + (error "py-custom-temp-directory not an existing directory")))) + (and (funcall ok (getenv "TMPDIR")) + (setq erg (getenv "TMPDIR"))) + (and (funcall ok (getenv "TEMP/TMP")) + (setq erg (getenv "TEMP/TMP"))) + (and (funcall ok "/usr/tmp") + (setq erg "/usr/tmp")) + (and (funcall ok "/tmp") + (setq erg "/tmp")) + (and (funcall ok "/var/tmp") + (setq erg "/var/tmp")) + (and (eq system-type 'darwin) + (funcall ok "/var/folders") + (setq erg "/var/folders")) + (and (or (eq system-type 'ms-dos)(eq system-type 'windows-nt)) + (funcall ok (concat "c:" (char-to-string py-separator-char) "Users")) + (setq erg (concat "c:" (char-to-string py-separator-char) "Users"))) + ;; (funcall ok ".") + (error + "Couldn't find a usable temp directory -- set `py-temp-directory'")) + (when erg (setq py-temp-directory erg))) + "*Directory used for temporary files created by a *Python* process. +By default, guesses the first directory from this list that exists and that you +can write into: the value (if any) of the environment variable TMPDIR, +/usr/tmp, /tmp, /var/tmp, or the current directory. + +`py-custom-temp-directory' will take precedence when setq ") + +(defcustom py-beep-if-tab-change t + "*Ring the bell if `tab-width' is changed. +If a comment of the form + + \t# vi:set tabsize=<number>: + +is found before the first code line when the file is entered, and the +current value of (the general Emacs variable) `tab-width' does not +equal <number>, `tab-width' is set to <number>, a message saying so is +displayed in the echo area, and if `py-beep-if-tab-change' is non-nil +the Emacs bell is also rung as a warning." + :type 'boolean + :group 'python-mode) + +(defcustom py-jump-on-exception t + "*Jump to innermost exception frame in *Python Output* buffer. +When this variable is non-nil and an exception occurs when running +Python code synchronously in a subprocess, jump immediately to the +source code of the innermost traceback frame." + :type 'boolean + :group 'python-mode) + +(defcustom py-ask-about-save t + "If not nil, ask about which buffers to save before executing some code. +Otherwise, all modified buffers are saved without asking." + :type 'boolean + :group 'python-mode) + +(defcustom py-backspace-function 'backward-delete-char-untabify + "*Function called by `py-electric-backspace' when deleting backwards." + :type 'function + :group 'python-mode) + +(defcustom py-delete-function 'delete-char + "*Function called by `py-electric-delete' when deleting forwards." + :type 'function + :group 'python-mode) + +(defcustom py-pdbtrack-do-tracking-p t + "*Controls whether the pdbtrack feature is enabled or not. +When non-nil, pdbtrack is enabled in all comint-based buffers, +e.g. shell buffers and the *Python* buffer. When using pdb to debug a +Python program, pdbtrack notices the pdb prompt and displays the +source file and line that the program is stopped at, much the same way +as gud-mode does for debugging C programs with gdb." + :type 'boolean + :group 'python-mode) +(make-variable-buffer-local 'py-pdbtrack-do-tracking-p) + +(defcustom py-pdbtrack-filename-mapping nil + "Supports mapping file paths when opening file buffers in pdbtrack. +When non-nil this is an alist mapping paths in the Python interpreter +to paths in Emacs." + :type 'alist + :group 'python-mode) + +(defcustom py-pdbtrack-minor-mode-string " PDB" + "*String to use in the minor mode list when pdbtrack is enabled." + :type 'string + :group 'python-mode) + +(defcustom py-import-check-point-max + 20000 + "Maximum number of characters to search for a Java-ish import statement. +When `python-mode' tries to calculate the shell to use (either a +CPython or a Jython shell), it looks at the so-called `shebang' line +-- i.e. #! line. If that's not available, it looks at some of the +file heading imports to see if they look Java-like." + :type 'integer + :group 'python-mode) + +(defcustom py-jython-packages + '("java" "javax") + "Imported packages that imply `jython-mode'." + :type '(repeat string) + :group 'python-mode) +(make-obsolete-variable 'py-jpython-packages 'py-jython-packages nil) + +(defcustom py-current-defun-show t + "If `py-current-defun' should jump to the definition, highlight it while waiting PY-WHICH-FUNC-DELAY seconds, before returning to previous position. + +Default is `t'." + + :type 'boolean + :group 'python-mode) + +(defcustom py-current-defun-delay 2 + "When called interactively, `py-current-defun' should wait PY-WHICH-FUNC-DELAY seconds at the definition name found, before returning to previous position. " + + :type 'number + :group 'python-mode) + +(defcustom py-send-receive-delay 5 + "Seconds to wait for output, used by `python-send-receive'. " + + :type 'number + :group 'python-mode) + +(defvar py-exec-command nil + "Mode commands will set this. ") +(make-variable-buffer-local 'py-exec-command) + +(defvar py-exec-string-command nil + "Mode commands will set this. ") +(make-variable-buffer-local 'py-exec-string-command) + +(defvar py-which-bufname "Python") +(make-variable-buffer-local 'py-which-bufname) + +(defcustom py-master-file nil + "If non-nil, \\[py-execute-buffer] executes the named +master file instead of the buffer's file. If the file name has a +relative path, the value of variable `default-directory' for the +buffer is prepended to come up with a file name. + +Beside you may set this variable in the file's local +variable section, e.g.: + + # Local Variables: + # py-master-file: \"master.py\" + # End: + +" + :type 'string + :group 'python-mode) +(make-variable-buffer-local 'py-master-file) + +(defvar py-pychecker-history nil) +(defcustom py-pychecker-command "pychecker" + "*Shell command used to run Pychecker." + :type 'string + :group 'python-mode + :tag "Pychecker Command") + +(defcustom py-pychecker-command-args '("--stdlib") + "*List of string arguments to be passed to pychecker." + :type '(repeat string) + :group 'python-mode + :tag "Pychecker Command Args") + +(defvar py-pep8-history nil) +(defcustom py-pep8-command "pep8" + "*Shell command used to run pep8." + :type 'string + :group 'python-mode + :tag "PEP 8 Command") + +(defcustom py-pep8-command-args '("") + "*List of string arguments to be passed to pep8. + +Default is \"\" " + :type '(repeat string) + :group 'python-mode + :tag "PEP 8 Command Args") + +(defvar py-pyflakespep8-history nil) +(defcustom py-pyflakespep8-command (concat py-install-directory "pyflakespep8.py") + "*Shell command used to run `pyflakespep8'." + :type 'string + :group 'python-mode + :tag "Pyflakespep8 Command") + +(defcustom py-pyflakespep8-command-args '("") + "*List of string arguments to be passed to pyflakespep8. + +Default is \"\" " + :type '(repeat string) + :group 'python-mode + :tag "Pyflakes-pep8 Command Args") + +(defvar py-pyflakes-history nil) +(defcustom py-pyflakes-command "pyflakes" + "*Shell command used to run Pyflakes." + :type 'string + :group 'python-mode + :tag "Pyflakes Command") + +(defcustom py-pyflakes-command-args '("") + "*List of string arguments to be passed to pyflakes. + +Default is \"\" " + :type '(repeat string) + :group 'python-mode + :tag "Pyflakes Command Args") + +(defcustom py-pep8-command-args '("") + "*List of string arguments to be passed to pylint. + +Default is \"\" " + :type '(repeat string) + :group 'python-mode + :tag "PEP 8 Command Args") + +(defvar py-pylint-history nil) +(defcustom py-pylint-command "pylint" + "*Shell command used to run Pylint." + :type 'string + :group 'python-mode + :tag "Pylint Command") + +(defcustom py-pylint-command-args '("--errors-only") + "*List of string arguments to be passed to pylint. + +Default is \"--errors-only\" " + :type '(repeat string) + :group 'python-mode + :tag "Pylint Command Args") + +(defvar py-shell-alist + '(("jython" . 'jython) + ("python" . 'cpython)) + "*Alist of interpreters and python shells. Used by `py-choose-shell' +to select the appropriate python interpreter mode for a file.") + +(defcustom py-shell-input-prompt-1-regexp "^>>> " + "*A regular expression to match the input prompt of the shell." + :type 'string + :group 'python-mode) + +(defcustom py-shell-input-prompt-2-regexp "^[.][.][.] " + "*A regular expression to match the input prompt of the shell after the + first line of input." + :type 'string + :group 'python-mode) + +(defcustom py-shell-switch-buffers-on-execute-p t + "When non-nil switch to the new Python shell. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-switch-buffers-on-execute-p nil + "When non-nil switch to the Python output buffer. " + + :type 'boolean + :group 'python-mode) + +(defcustom py-split-windows-on-execute-p t + "When non-nil split windows. " + :type 'boolean + :group 'python-mode) + +(defcustom py-split-windows-on-execute-function 'split-window-vertically + "How window should get splitted to display results of py-execute-... functions. " + :type '(choice (const :tag "split-window-vertically" split-window-vertically) + (const :tag "split-window-horizontally" split-window-horizontally) + ) + :group 'python-mode) +(make-variable-buffer-local 'py-split-windows-on-execute-function) + +(defcustom py-hide-show-keywords + '("class" "def" "elif" "else" "except" + "for" "if" "while" "finally" "try" + "with") + "Keywords composing visible heads. +Also used by (minor-)outline-mode " + :type '(repeat string) + :group 'python-mode) + +(defcustom py-hide-show-hide-docstrings t + "*Controls if doc strings can be hidden by hide-show" + :type 'boolean + :group 'python-mode) + +(defcustom python-mode-hook nil + "Hook run when entering Python mode." + :group 'python-mode + :type 'hook) + +(custom-add-option 'python-mode-hook 'imenu-add-menubar-index) +(custom-add-option 'python-mode-hook + (lambda () + "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) + +(defcustom py-imenu-create-index-p nil + "Non-nil means Python mode creates and displays an index menu of functions and global variables. " + :type 'boolean + :group 'python-mode) + +(defcustom py-shell-name "python" + "A PATH/TO/EXECUTABLE or default value `py-shell' may look for, if no shell is specified by command. " + :type 'string + :group 'python-mode) +(make-variable-buffer-local 'py-shell-name) + +(defcustom py-shell-toggle-1 py-shell-name + "A PATH/TO/EXECUTABLE or default value used by `py-toggle-shell'. " + :type 'string + :group 'python-mode) +(make-variable-buffer-local 'py-shell-toggle-1) + +(defcustom py-shell-toggle-2 "python3" + "A PATH/TO/EXECUTABLE or default value used by `py-toggle-shell'. " + :type 'string + :group 'python-mode) +(make-variable-buffer-local 'py-shell-toggle-2) + +(defcustom py-source-modes '(python-mode jython-mode) + "Used to determine if a buffer contains Python source code. + +If a file is loaded into a buffer that is in one of these major modes, it is considered Python source by `py-load-file', which uses the +value to determine defaults." + :type '(repeat function) + :group 'python-mode) + +(defcustom py-shell-prompt-alist + '(("ipython" . "^In \\[[0-9]+\\]: *") + (t . "^>>> ")) + "Alist of Python input prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `py-shell-name' for the python process and +REGEXP is a regular expression matching the Python prompt. +PROGRAM can also be t, which specifies the default when no other +element matches `py-shell-name'." + :type 'string + :group 'python-mode) + +(defcustom py-shell-continuation-prompt-alist + '(("ipython" . "^ [.][.][.]+: *") + (t . "^[.][.][.] ")) + "Alist of Python continued-line prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `py-shell-name' for the python process and +REGEXP is a regular expression matching the Python prompt for +continued lines. +PROGRAM can also be t, which specifies the default when no other +element matches `py-shell-name'." + :type 'string + :group 'python-mode) + +;; ipython.el +(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.") + +(defcustom py-match-paren-mode nil + "*Non-nil means, cursor will jump to beginning or end of a block. +This vice versa, to beginning first. +Sets `py-match-paren-key' in python-mode-map. +Customize `py-match-paren-key' which key to use. " + :type 'boolean + :group 'python-mode) + +(defcustom py-match-paren-key "%" + "*String used by \\[comment-region] to comment out a block of code. +This should follow the convention for non-indenting comment lines so +that the indentation commands won't get confused (i.e., the string +should be of the form `#x...' where `x' is not a blank or a tab, and +`...' is arbitrary). However, this string should not end in whitespace." + :type 'string + :group 'python-mode) + +(defcustom py-kill-empty-line t + "*If t, py-indent-forward-line kills empty lines. " + :type 'boolean + :group 'python-mode) + +(defcustom py-remove-cwd-from-path t + "Whether to allow loading of Python modules from the current directory. +If this is non-nil, Emacs removes '' from sys.path when starting +an inferior Python process. This is the default, for security +reasons, as it is easy for the Python process to be started +without the user's realization (e.g. to perform completion)." + :type 'boolean + :group 'python-mode) + +(defcustom py-imenu-show-method-args-p nil + "*Controls echoing of arguments of functions & methods in the Imenu buffer. +When non-nil, arguments are printed." + :type 'boolean + :group 'python-mode) + +(defcustom python-default-interpreter 'cpython + "*Which Python interpreter is used by default. +The value for this variable can be either `cpython' or `jpython'. + +When the value is `cpython', the variables `python-python-command' and +`python-python-command-args' are consulted to determine the interpreter +and arguments to use. + +When the value is `jpython', the variables `python-jpython-command' and +`python-jpython-command-args' are consulted to determine the interpreter +and arguments to use. + +Note that this variable is consulted only the first time that a Python +mode buffer is visited during an Emacs session. After that, use +\\[py-toggle-shell] to change the interpreter shell." + :type '(choice (const :tag "Python (a.k.a. CPython)" cpython) + (const :tag "JPython" jpython)) + :group 'python-mode) + +(defcustom python-python-command-args '("-i") + "*List of string arguments to be used when starting a Python shell." + :type '(repeat string) + :group 'python-mode) + +(defcustom python-jython-command-args '("-i") + "*List of string arguments to be used when starting a Jython shell." + :type '(repeat string) + :group 'python-mode + :tag "JPython Command Args") + +(defcustom python-pdbtrack-do-tracking-p t + "*Controls whether the pdbtrack feature is enabled or not. + +When non-nil, pdbtrack is enabled in all comint-based buffers, +e.g. shell interaction buffers and the *Python* buffer. + +When using pdb to debug a Python program, pdbtrack notices the +pdb prompt and presents the line in the source file where the +program is stopped in a pop-up buffer. It's similar to what +gud-mode does for debugging C programs with gdb, but without +having to restart the program." + :type 'boolean + :group 'python-mode) +(make-variable-buffer-local 'python-pdbtrack-do-tracking-p) + +(defcustom python-pdbtrack-minor-mode-string " PDB" + "*Minor-mode sign to be displayed when pdbtrack is active." + :type 'string + :group 'python-mode) + +(defcustom python-shell-prompt-alist + '(("ipython" . "^In \\[[0-9]+\\]: *") + (t . "^>>> ")) + "Alist of Python input prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `python-python-command' for the python process and +REGEXP is a regular expression matching the Python prompt. +PROGRAM can also be t, which specifies the default when no other +element matches `python-python-command'." + :type 'string + :group 'python-mode) + +(defcustom python-shell-continuation-prompt-alist + '(("ipython" . "^ [.][.][.]+: *") + (t . "^[.][.][.] ")) + "Alist of Python continued-line prompts. +Each element has the form (PROGRAM . REGEXP), where PROGRAM is +the value of `python-python-command' for the python process and +REGEXP is a regular expression matching the Python prompt for +continued lines. +PROGRAM can also be t, which specifies the default when no other +element matches `python-python-command'." + :type 'string + :group 'python-mode) + +(defcustom python-python-command "python" + "Shell command to run Python interpreter. +Any arguments can't contain whitespace." + :group 'python-mode + :type 'string) + +(defcustom python-jython-command "jython" + "Shell command to run Jython interpreter. +Any arguments can't contain whitespace." + :group 'python-mode + :type 'string) + +(defcustom py-history-filter-regexp "\\`\\s-*\\S-?\\S-?\\s-*\\'" + "Input matching this regexp is not saved on the history list. +Default ignores all inputs of 0, 1, or 2 non-blank characters." + :type 'regexp + :group 'python-mode) + +(defcustom python-remove-cwd-from-path t + "Whether to allow loading of Python modules from the current directory. +If this is non-nil, Emacs removes '' from sys.path when starting +an inferior Python process. This is the default, for security +reasons, as it is easy for the Python process to be started +without the user's realization (e.g. to perform completion)." + :type 'boolean + :group 'python-mode + :version "23.3") + +(defcustom python-source-modes '(python-mode jython-mode) + "Used to determine if a buffer contains Python source code. + +If a file is loaded into a buffer that is in one of these major modes, it is considered Python source by `py-load-file', which uses the +value to determine defaults." + :type '(repeat function) + :group 'python-mode) + +(defcustom python-jython-packages '("java" "javax" "org" "com") + "Packages implying `jython-mode'. +If these are imported near the beginning of the buffer, `python-mode' +actually punts to `jython-mode'." + :type '(repeat string) + :group 'python-mode) + +(defface py-number-face + '((t (:inherit default))) + ;; '((t (:inherit 'font-lock-variable-name-face))) + "Highlight numbers. " + :group 'python-mode) +(defvar py-number-face 'py-number-face) + +(defface py-XXX-tag-face + '((t (:inherit font-lock-string-face))) + "XXX\\|TODO\\|FIXME " + :group 'python-mode) +(defvar py-XXX-tag-face 'py-XXX-tag-face) + +;; ;; Face for None, True, False, self, and Ellipsis +(defface py-pseudo-keyword-face + '((t (:inherit font-lock-keyword-face))) + "Face for pseudo keywords in Python mode, like self, True, False, Ellipsis." + :group 'python-mode) +(defvar py-pseudo-keyword-face 'py-pseudo-keyword-face) + +(defface py-variable-name-face + '((t (:inherit default))) + ;; '((t (:inherit 'font-lock-variable-name-face))) + "Face method decorators." + :group 'python-mode) +(defvar py-variable-name-face 'py-variable-name-face) + +;; PEP 318 decorators +(defface py-decorators-face + '((t (:inherit font-lock-keyword-face))) + "Face method decorators." + :group 'python-mode) +(defvar py-decorators-face 'py-decorators-face) + +;; Face for builtins +(defface py-builtins-face + '((t (:inherit font-lock-builtin-face))) + "Face for builtins like TypeError, object, open, and exec." + :group 'python-mode) +(defvar py-builtins-face 'py-builtins-face) + +(defface py-class-name-face + '((t (:inherit font-lock-type-face))) + "Face for classes." + :group 'python-mode) +(defvar py-class-name-face 'py-class-name-face) + +;; XXX, TODO, and FIXME comments and such +(defface py-exception-name-face + '((t (:inherit font-lock-builtin-face))) + "." + :group 'python-mode) +(defvar py-exception-name-face 'py-exception-name-face) + +(defcustom py-use-local-default nil + "If `t', py-shell will use `py-shell-local-path' instead + of default Python. + +Making switch between several virtualenv's easier, + `python-mode' should deliver an installer, so named-shells pointing to virtualenv's will be available. " + :type 'boolean + :group 'python-mode) + +;; (defcustom python-load-extended-executes-p t +;; "If commands from `python-extended-executes.el' should be loaded. +;; +;; Default is `t'. +;; Provides commands executing buffers code at different conditions, thus avoids customization of `py-shell-name', `py-switch-buffers-on-execute-p'. " +;; +;; :type 'boolean +;; :group 'python-mode) + +(defcustom py-shell-local-path "" + "If `py-use-local-default' is non-nil, `py-shell' will use EXECUTABLE indicated here incl. path. " + + :type 'string + :group 'python-mode) + +;;; highlight-indentation.el --- Function for highlighting indentation +;; Original Author: Anton Johansson <anton.johansson@gmail.com> - http://antonj.se +;; Created: Dec 15 23:42:04 2010 +;; URL: https://github.com/antonj/Highlight-Indentation-for-Emacs + +(defcustom highlight-indentation nil + "If level of indentation should be displayed at start. +Toggle buffer local status via `M-x highlight-indentation' during session. " + + :type 'boolean + :group 'python-mode) +(make-variable-buffer-local 'highlight-indentation) + +(defvar highlight-indent-active nil) +(make-variable-buffer-local 'highlight-indent-active) + +(defface highlight-indent-face + '((((class color) (background dark)) + (:background "grey33")) + (((class color) (background light)) + (:background "grey"))) + "Basic face for highlighting indentation guides.") + +(defvar highlight-indent-offset 4) +(setq-default highlight-indent-offset 4) + +(defvar ruby-indent-level nil) +(defvar nxml-child-indent nil) + +(defun highlight-indentation-on () + "Make sure `highlight-indentation' is on. " + (interactive) + (set (make-local-variable 'highlight-indent-active) nil) + (highlight-indentation) + (when (called-interactively-p 'any) + (message "highlight-indentation ON"))) + +(defun highlight-indentation-off () + "Make sure `highlight-indentation' is off. " + (interactive) + (set (make-local-variable 'highlight-indent-active) t) + (highlight-indentation) + (when (called-interactively-p 'any) + (message "highlight-indentation OFF"))) + +(defun highlight-indentation (&optional indent-width) + "Toggle highlight indentation. +Optional argument INDENT-WIDTH specifies which indentation +level (spaces only) should be highlighted, if omitted +indent-width will be guessed from current major-mode" + (interactive "P") + (let ((re (format "\\( \\) \\{%s\\}" (- highlight-indent-offset 1)))) + (if (not highlight-indent-active) + (progn ;; Toggle on + (set (make-local-variable 'highlight-indent-offset) + (if indent-width + indent-width + ;; Set indentation offset according to major mode + (cond ((eq major-mode 'python-mode) + (if (boundp 'python-indent) + python-indent + py-indent-offset)) + ((eq major-mode 'ruby-mode) + ruby-indent-level) + ((eq major-mode 'nxml-mode) + nxml-child-indent) + ((local-variable-p 'c-basic-offset) + c-basic-offset) + (t + (default-value 'highlight-indent-offset))))) + (set (make-local-variable 'highlight-indent-active) t) + (if (featurep 'xemacs) + (font-lock-add-keywords nil `((,re (1 'paren-face-match)))) + (font-lock-add-keywords nil `((,re (1 'highlight-indent-face))))) + (message (format "highlight-indentation with indent-width %s" + highlight-indent-offset))) + ;; Toggle off + (set (make-local-variable 'highlight-indent-active) nil) + (if (featurep 'xemacs) + (font-lock-remove-keywords nil `((,re (1 'paren-face-match)))) + (font-lock-remove-keywords nil `((,re (1 'highlight-indent-face))))) + (message "highlight-indentation OFF")) + (font-lock-fontify-buffer))) + +(defcustom py-underscore-word-syntax-p t + "If underscore chars should be of syntax-class `word', not of `symbol'. + +Underscores in word-class makes `forward-word' etc. travel the indentifiers. Default is `t'. + +See bug report at launchpad, lp:940812 " + :type 'boolean + :group 'python-mode) + +(defcustom py-edit-only-p nil + "When `t' `python-mode' will not take resort nor check for installed Python executables. Default is nil. + +See bug report at launchpad, lp:944093. " + :type 'boolean + :group 'python-mode) + +(defvar py-force-local-shell-p nil + "Used internally, see `toggle-force-local-shell'. ") + +(defcustom py-force-py-shell-name-p nil + "When `t', execution with kind of Python specified in `py-shell-name' is enforced, possibly shebang doesn't take precedence. " + + :type 'boolean + :group 'python-mode) + +(defvar python-mode-v5-behavior nil) +(defcustom python-mode-v5-behavior-p nil + "Execute region through `shell-command-on-region' as +v5 did it - lp:990079. This might fail with certain chars - see UnicodeEncodeError lp:550661" + + :type 'boolean + :group 'python-mode) + +(defcustom py-warn-tmp-files-left-p nil + "Messages a warning, when `py-temp-directory' contains files susceptible being left by previous Python-mode sessions. See also lp:987534 " + :type 'boolean + :group 'python-mode) + +(defcustom py-ipython-execute-delay 0.3 + "Delay needed by execute functions when no IPython shell is running. " + :type 'float + :group 'python-mode) + +;;; Derived Python's flying circus support for Emacs +;; Employed for completion in Python3 shells +;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. + +;; Original author: Fabián E. Gallina <fabian@anue.biz> +;; URL: https://github.com/fgallina/python.el + +(defcustom python-shell-buffer-name "Python" + "Default buffer name for Python interpreter." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-interpreter "python" + "Default Python interpreter for shell." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-internal-buffer-name "Python Internal" + "Default buffer name for the Internal Python interpreter." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-interpreter-args "-i" + "Default arguments for the Python interpreter." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-prompt-regexp ">>> " + "Regular Expression matching top\-level input prompt of python shell. +It should not contain a caret (^) at the beginning." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-prompt-block-regexp "[.][.][.] " + "Regular Expression matching block input prompt of python shell. +It should not contain a caret (^) at the beginning." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-prompt-output-regexp "" + "Regular Expression matching output prompt of python shell. +It should not contain a caret (^) at the beginning." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " + "Regular Expression matching pdb input prompt of python shell. +It should not contain a caret (^) at the beginning." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-send-setup-max-wait 5 + "Seconds to wait for process output before code setup. +If output is received before the especified time then control is +returned in that moment and not after waiting." + :type 'integer + :group 'python-mode + :safe 'integerp) + +(defcustom python-shell-process-environment nil + "List of environment variables for Python shell. +This variable follows the same rules as `process-environment' +since it merges with it before the process creation routines are +called. When this variable is nil, the Python shell is run with +the default `process-environment'." + :type '(repeat string) + :group 'python-mode + :safe 'listp) + +(defcustom python-shell-extra-pythonpaths nil + "List of extra pythonpaths for Python shell. +The values of this variable are added to the existing value of +PYTHONPATH in the `process-environment' variable." + :type '(repeat string) + :group 'python-mode + :safe 'listp) + +(defcustom python-shell-exec-path nil + "List of path to search for binaries. +This variable follows the same rules as `exec-path' since it +merges with it before the process creation routines are called. +When this variable is nil, the Python shell is run with the +default `exec-path'." + :type '(repeat string) + :group 'python-mode + :safe 'listp) + +(defcustom python-shell-virtualenv-path nil + "Path to virtualenv root. +This variable, when set to a string, makes the values stored in +`python-shell-process-environment' and `python-shell-exec-path' +to be modified properly so shells are started with the specified +virtualenv." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-ffap-setup-code + "def __FFAP_get_module_path(module): + try: + import os + path = __import__(module).__file__ + if path[-4:] == '.pyc' and os.path.exists(path[0:-1]): + path = path[:-1] + return path + except: + return ''" + "Python code to get a module path." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-ffap-string-code + "__FFAP_get_module_path('''%s''')\n" + "Python code used to get a string with the path of a module." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defvar python--prompt-regexp nil) + +(defvar python-command python-python-command + "Actual command used to run Python. +May be `python-python-command' or `python-jython-command', possibly +modified by the user. Additional arguments are added when the command +is used by `run-python' et al.") + +(defvar python-buffer nil + "*The current Python process buffer. + +Commands that send text from source buffers to Python processes have +to choose a process to send to. This is determined by buffer-local +value of `python-buffer'. If its value in the current buffer, +i.e. both any local value and the default one, is nil, `run-python' +and commands that send to the Python process will start a new process. + +Whenever \\[run-python] starts a new process, it resets the default +value of `python-buffer' to be the new process's buffer and sets the +buffer-local value similarly if the current buffer is in Python mode +or Inferior Python mode, so that source buffer stays associated with a +specific sub-process. ") +(make-variable-buffer-local 'python-buffer) + +(defun python-ffap-module-path (module) + "Function for `ffap-alist' to return path for MODULE." + (let ((process (or + (and (eq major-mode 'inferior-python-mode) + (get-buffer-process (current-buffer))) + (python-shell-get-process)))) + (if (not process) + nil + (let ((module-file + (python-shell-send-string-no-output + (format python-ffap-string-code module) process))) + (when module-file + (substring-no-properties module-file 1 -1)))))) + +(eval-after-load "ffap" + '(progn + (push '(python-mode . python-ffap-module-path) ffap-alist) + (push '(inferior-python-mode . python-ffap-module-path) ffap-alist))) + +(defcustom python-shell-setup-codes '(python-shell-completion-setup-code + python-ffap-setup-code + python-eldoc-setup-code) + "List of code run by `py-shell-send-setup-codes'." + :type '(repeat symbol) + :group 'python-mode + :safe 'listp) + +(defcustom python-shell-compilation-regexp-alist + `((,(rx line-start (1+ (any " \t")) "File \"" + (group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c + "\", line " (group (1+ digit))) + 1 2) + (,(rx " in file " (group (1+ not-newline)) " on line " + (group (1+ digit))) + 1 2) + (,(rx line-start "> " (group (1+ (not (any "(\"<")))) + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + 1 2)) + "`compilation-error-regexp-alist' for inferior Python." + :type '(alist string) + :group 'python-mode) + +(defvar python-mode-syntax-table nil + "Syntax table for Python files.") + +(setq python-mode-syntax-table + (let ((table (make-syntax-table))) + ;; Give punctuation syntax to ASCII that normally has symbol + ;; syntax or has word syntax and isn't a letter. + (let ((symbol (string-to-syntax "_")) + (sst (standard-syntax-table))) + (dotimes (i 128) + (unless (= i ?_) + (if (equal symbol (aref sst i)) + (modify-syntax-entry i "." table))))) + (modify-syntax-entry ?$ "." table) + (modify-syntax-entry ?% "." table) + ;; exceptions + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?' "\"" table) + (modify-syntax-entry ?` "$" table) + (when py-underscore-word-syntax-p + (modify-syntax-entry ?_ "w" table)) + table)) + +(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'.") +(defvar outline-heading-end-regexp) +(defvar eldoc-documentation-function) + +;; Stolen from org-mode +(defun python-util-clone-local-variables (from-buffer &optional regexp) + "Clone local variables from FROM-BUFFER. +Optional argument REGEXP selects variables to clone and defaults +to \"^python-\"." + (mapc + (lambda (pair) + (and (symbolp (car pair)) + (string-match (or regexp "^python-") + (symbol-name (car pair))) + (set (make-local-variable (car pair)) + (cdr pair)))) + (buffer-local-variables from-buffer))) + +(defun py-send-shell-setup-code () + "Send all setup code for shell. +This function takes the list of setup code to send from the +`python-shell-setup-codes' list." + (let ((process (get-buffer-process (current-buffer)))) + ;; (accept-process-output process python-shell-send-setup-max-wait) + (accept-process-output process 1) + (dolist (code python-shell-setup-codes) + (when code + (py-send-string (symbol-value code) process))))) + +(defun python-shell-get-process-name (dedicated) + "Calculate the appropiate process name for inferior Python process. +If DEDICATED is t and the variable `buffer-file-name' is non-nil +returns a string with the form +`python-shell-buffer-name'[variable `buffer-file-name'] else +returns the value of `python-shell-buffer-name'. After +calculating the process name adds the buffer name for the process +in the `same-window-buffer-names' list." + (let ((process-name + (if (and dedicated + buffer-file-name) + (format "%s[%s]" python-shell-buffer-name buffer-file-name) + (format "%s" python-shell-buffer-name)))) + (add-to-list 'same-window-buffer-names (purecopy + (format "*%s*" process-name))) + process-name)) + +(defun python-shell-internal-get-process-name () + "Calculate the appropiate process name for Internal Python process. +The name is calculated from `python-shell-global-buffer-name' and +a hash of all relevant global shell settings in order to ensure +uniqueness for different types of configurations." + (format "%s [%s]" + python-shell-internal-buffer-name + (md5 + (concat + (python-shell-parse-command) + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-output-regexp + (mapconcat #'symbol-value python-shell-setup-codes "") + (mapconcat #'identity python-shell-process-environment "") + (mapconcat #'identity python-shell-extra-pythonpaths "") + (mapconcat #'identity python-shell-exec-path "") + (or python-shell-virtualenv-path "") + (mapconcat #'identity python-shell-exec-path ""))))) + +(defun python-shell-parse-command () + "Calculate the string used to execute the inferior Python process." + (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) + +(defun python-shell-calculate-process-environment () + "Calculate process environment given `python-shell-virtualenv-path'." + (let ((process-environment (append + python-shell-process-environment + process-environment nil)) + (virtualenv (if python-shell-virtualenv-path + (directory-file-name python-shell-virtualenv-path) + nil))) + (when python-shell-extra-pythonpaths + (setenv "PYTHONPATH" + (format "%s%s%s" + (mapconcat 'identity + python-shell-extra-pythonpaths + path-separator) + path-separator + (or (getenv "PYTHONPATH") "")))) + (if (not virtualenv) + process-environment + (setenv "PYTHONHOME" nil) + (setenv "PATH" (format "%s/bin%s%s" + virtualenv path-separator + (or (getenv "PATH") ""))) + (setenv "VIRTUAL_ENV" virtualenv)) + process-environment)) + +(defun python-shell-calculate-exec-path () + "Calculate exec path given `python-shell-virtualenv-path'." + (let ((path (append python-shell-exec-path + exec-path nil))) + (if (not python-shell-virtualenv-path) + path + (cons (format "%s/bin" + (directory-file-name python-shell-virtualenv-path)) + path)))) + +(defcustom python-shell-send-setup-max-wait 5 + "Seconds to wait for process output before code setup. +If output is received before the especified time then control is +returned in that moment and not after waiting." + :type 'integer + :group 'python-mode + :safe 'integerp) + +(defun python-shell-make-comint (cmd proc-name &optional pop) + "Create a python shell comint buffer. +CMD is the python command to be executed and PROC-NAME is the +process name the comint buffer will get. After the comint buffer +is created the `inferior-python-mode' is activated. If POP is +non-nil the buffer is shown." + (save-excursion + (let* ((proc-buffer-name (format "*%s*" proc-name)) + (process-environment (python-shell-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path))) + (when (not (comint-check-proc proc-buffer-name)) + (let* ((cmdlist (split-string-and-unquote cmd)) + (buffer (apply 'make-comint proc-name (car cmdlist) nil + (cdr cmdlist))) + (current-buffer (current-buffer))) + (with-current-buffer buffer + (inferior-python-mode) + (python-util-clone-local-variables current-buffer)))) + (when pop + (pop-to-buffer proc-buffer-name)) + proc-buffer-name))) + +(defcustom python-shell-completion-setup-code + "try: + import readline +except ImportError: + def __COMPLETER_all_completions(text): [] +else: + import rlcompleter + readline.set_completer(rlcompleter.Completer().complete) + def __COMPLETER_all_completions(text): + import sys + completions = [] + try: + i = 0 + while True: + res = readline.get_completer()(text, i) + if not res: break + i += 1 + completions.append(res) + except NameError: + pass + return completions" + "Code used to setup completion in inferior Python processes." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-completion-string-code + "';'.join(__COMPLETER_all_completions('''%s'''))\n" + "Python code used to get a string of completions separated by semicolons." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-shell-module-completion-string-code "" + "Python code used to get completions separated by semicolons for imports. + +For IPython v0.11, add the following line to +`python-shell-completion-setup-code': + +from IPython.core.completerlib import module_completion + +and use the following as the value of this variable: + +';'.join(module_completion('''%s'''))\n" + :type 'string + :group 'python-mode + :safe 'stringp) + +(defvar python-completion-original-window-configuration nil) + +(defun run-python-internal () + "Run an inferior Internal Python process. +Input and output via buffer named after +`python-shell-internal-buffer-name' and what +`python-shell-internal-get-process-name' returns. This new kind +of shell is intended to be used for generic communication related +to defined configurations. The main difference with global or +dedicated shells is that these ones are attached to a +configuration, not a buffer. This means that can be used for +example to retrieve the sys.path and other stuff, without messing +with user shells. Runs the hook +`inferior-python-mode-hook' (after the `comint-mode-hook' is +run). \(Type \\[describe-mode] in the process buffer for a list +of commands.)" + (interactive) + (set-process-query-on-exit-flag + (get-buffer-process + (python-shell-make-comint + (python-shell-parse-command) + (python-shell-internal-get-process-name))) nil)) + +(defun python-shell-get-process () + "Get inferior Python process for current buffer and return it." + (let* ((dedicated-proc-name (python-shell-get-process-name t)) + (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) + (global-proc-name (python-shell-get-process-name nil)) + (global-proc-buffer-name (format "*%s*" global-proc-name)) + (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) + (global-running (comint-check-proc global-proc-buffer-name))) + ;; Always prefer dedicated + (get-buffer-process (or (and dedicated-running dedicated-proc-buffer-name) + (and global-running global-proc-buffer-name))))) + +(defun python-shell-get-or-create-process () + "Get or create an inferior Python process for current buffer and return it." + (let* ((old-buffer (current-buffer)) + (dedicated-proc-name (python-shell-get-process-name t)) + (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) + (global-proc-name (python-shell-get-process-name nil)) + (global-proc-buffer-name (format "*%s*" global-proc-name)) + (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) + (global-running (comint-check-proc global-proc-buffer-name)) + (current-prefix-arg 4)) + (when (and (not dedicated-running) (not global-running)) + (if (call-interactively 'run-python) + (setq dedicated-running t) + (setq global-running t))) + ;; Always prefer dedicated + (switch-to-buffer old-buffer) + (get-buffer-process (if dedicated-running + dedicated-proc-buffer-name + global-proc-buffer-name)))) + +(defun python-shell-internal-get-or-create-process () + "Get or create an inferior Internal Python process." + (let* ((proc-name (python-shell-internal-get-process-name)) + (proc-buffer-name (format "*%s*" proc-name))) + (run-python-internal) + (get-buffer-process proc-buffer-name))) + +(defun python-shell-send-string (string &optional process msg) + "Send STRING to inferior Python PROCESS. +When `py-verbose-p' and MSG is non-nil messages the first line of STRING." + (interactive "sPython command: ") + (let* ((process (or process (python-shell-get-or-create-process))) + (lines (split-string string "\n" t)) + ;; (temp-file-name (make-temp-file "py")) + (temp-file-name (concat (py-normalize-directory py-temp-directory) "psss-temp.py")) + (file-name (or (buffer-file-name) temp-file-name))) + ;; (when (and py-verbose-p msg) + ;; (message (format "Sent: %s..." (nth 0 lines))) + ;; ) + (if (> (length lines) 1) + (progn + (with-temp-file temp-file-name + (insert string) + (delete-trailing-whitespace)) + (python-shell-send-file file-name process temp-file-name)) + (comint-send-string process string) + (when (or (not (string-match "\n$" string)) + (string-match "\n[ \t].*\n?$" string)) + (comint-send-string process "\n"))) + ;; (when (file-readable-p temp-file-name) (delete-file temp-file-name)) + ) + ) + +(defun python-shell-send-string-no-output (string &optional process msg) + "Send STRING to PROCESS and inhibit output. +When MSG is non-nil messages the first line of STRING. Return +the output." + (let* ((output-buffer) + (process (or process (python-shell-get-or-create-process))) + (comint-preoutput-filter-functions + (append comint-preoutput-filter-functions + '(ansi-color-filter-apply + (lambda (string) + (setq output-buffer (concat output-buffer string)) + ""))))) + (python-shell-send-string string process msg) + (accept-process-output process 1) + (when output-buffer + (replace-regexp-in-string + (if (> (length python-shell-prompt-output-regexp) 0) + (format "\n*%s$\\|^%s\\|\n$" + python-shell-prompt-regexp + (or python-shell-prompt-output-regexp "")) + (format "\n*$\\|^%s\\|\n$" + python-shell-prompt-regexp)) + "" output-buffer)))) + +(defun python-shell-internal-send-string (string) + "Send STRING to the Internal Python interpreter. +Returns the output. See `python-shell-send-string-no-output'." + (python-shell-send-string-no-output + ;; Makes this function compatible with the old + ;; python-send-receive. (At least for CEDET). + (replace-regexp-in-string "_emacs_out +" "" string) + (python-shell-internal-get-or-create-process) nil)) + +(defun python-shell-send-region (start end) + "Send the region delimited by START and END to inferior Python process." + (interactive "r") + (let ((deactivate-mark nil)) + (python-shell-send-string (buffer-substring start end) nil t))) + +(defun python-shell-send-buffer () + "Send the entire buffer to inferior Python process." + (interactive) + (save-restriction + (widen) + (python-shell-send-region (point-min) (point-max)))) + +(defun python-shell-send-defun (arg) + "Send the current defun to inferior Python process. +When argument ARG is non-nil sends the innermost defun." + (interactive "P") + (save-excursion + (python-shell-send-region + (progn + (or (python-beginning-of-defun-function) + (progn (beginning-of-line) (point-marker)))) + (progn + (or (python-end-of-defun-function) + (progn (end-of-line) (point-marker))))))) + +(defun python-shell-send-file (file-name &optional process temp-file-name) + "Send FILE-NAME to inferior Python PROCESS. +If TEMP-FILE-NAME is passed then that file is used for processing +instead, while internally the shell will continue to use +FILE-NAME." + (interactive "fFile to send: ") + (let* ((process (or process (python-shell-get-or-create-process))) + (temp-file-name (when temp-file-name + (expand-file-name temp-file-name))) + (file-name (or (expand-file-name file-name) temp-file-name)) + py-python-command-args) + (when (not file-name) + (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) + (python-shell-send-string + (format + (concat "__pyfile = open('''%s''');" + "exec(compile(__pyfile.read(), '''%s''', 'exec'));" + "__pyfile.close()") + (or temp-file-name file-name) file-name) + process))) + +(defun python-shell-switch-to-shell () + "Switch to inferior Python process buffer." + (interactive) + (pop-to-buffer (process-buffer (python-shell-get-or-create-process)) t)) + +(defcustom python-pdbtrack-stacktrace-info-regexp + "^> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "Regular Expression matching stacktrace information. +Used to extract the current line and module being inspected." + :type 'string + :group 'python-mode + :safe 'stringp) + +;;; Eldoc + +(defcustom python-eldoc-setup-code + "def __PYDOC_get_help(obj): + try: + import inspect + if hasattr(obj, 'startswith'): + obj = eval(obj, globals()) + doc = inspect.getdoc(obj) + if not doc and callable(obj): + target = None + if inspect.isclass(obj) and hasattr(obj, '__init__'): + target = obj.__init__ + objtype = 'class' + else: + target = obj + objtype = 'def' + if target: + args = inspect.formatargspec( + *inspect.getargspec(target) + ) + name = obj.__name__ + doc = '{objtype} {name}{args}'.format( + objtype=objtype, name=name, args=args + ) + else: + doc = doc.splitlines()[0] + except: + doc = '' + try: + exec('print doc') + except SyntaxError: + print(doc)" + "Python code to setup documentation retrieval." + :type 'string + :group 'python-mode + :safe 'stringp) + +(defcustom python-eldoc-string-code + "__PYDOC_get_help('''%s''')\n" + "Python code used to get a string with the documentation of an object." + :type 'string + :group 'python-mode + :safe 'stringp) + +;;; Pdb +(defvar python-pdbtrack-tracked-buffer nil + "Variable containing the value of the current tracked buffer. +Never set this variable directly, use +`python-pdbtrack-set-tracked-buffer' instead.") +(make-variable-buffer-local 'python-pdbtrack-tracked-buffer) + +(defvar python-pdbtrack-buffers-to-kill nil + "List of buffers to be deleted after tracking finishes.") +(make-variable-buffer-local 'python-pdbtrack-buffers-to-kill) + +(defun python-pdbtrack-set-tracked-buffer (file-name) + "Set the buffer for FILE-NAME as the tracked buffer. +Internally it uses the `python-pdbtrack-tracked-buffer' variable. +Returns the tracked buffer." + (let ((file-buffer (get-file-buffer file-name))) + (if file-buffer + (setq python-pdbtrack-tracked-buffer file-buffer) + (setq file-buffer (find-file-noselect file-name)) + (when (not (member file-buffer python-pdbtrack-buffers-to-kill)) + (add-to-list 'python-pdbtrack-buffers-to-kill file-buffer))) + file-buffer)) + +(defun python-pdbtrack-comint-output-filter-function (output) + "Move overlay arrow to current pdb line in tracked buffer. +Argument OUTPUT is a string with the output from the comint process." + (when (not (string= output "")) + (let* ((full-output (ansi-color-filter-apply + (buffer-substring comint-last-input-end (point-max)))) + (line-number) + (file-name + (with-temp-buffer + (insert full-output) + (goto-char (point-min)) + ;; OK, this sucked but now it became a cool hack. The + ;; stacktrace information normally is on the first line + ;; but in some cases (like when doing a step-in) it is + ;; on the second. + (when (or (looking-at python-pdbtrack-stacktrace-info-regexp) + (and (forward-line) + (looking-at python-pdbtrack-stacktrace-info-regexp))) + (setq line-number (string-to-number + (match-string-no-properties 2))) + (match-string-no-properties 1))))) + (if (and file-name line-number) + (let* ((tracked-buffer (python-pdbtrack-set-tracked-buffer file-name)) + (shell-buffer (current-buffer)) + (tracked-buffer-window (get-buffer-window tracked-buffer)) + (tracked-buffer-line-pos)) + (with-current-buffer tracked-buffer + (set (make-local-variable 'overlay-arrow-string) "=>") + (set (make-local-variable 'overlay-arrow-position) (make-marker)) + (setq tracked-buffer-line-pos (progn + (goto-char (point-min)) + (forward-line (1- line-number)) + (point-marker))) + (when tracked-buffer-window + (set-window-point + tracked-buffer-window tracked-buffer-line-pos)) + (set-marker overlay-arrow-position tracked-buffer-line-pos)) + (pop-to-buffer tracked-buffer) + (switch-to-buffer-other-window shell-buffer)) + (when python-pdbtrack-tracked-buffer + (with-current-buffer python-pdbtrack-tracked-buffer + (set-marker overlay-arrow-position nil)) + (mapc #'(lambda (buffer) + (ignore-errors (kill-buffer buffer))) + python-pdbtrack-buffers-to-kill) + (setq python-pdbtrack-tracked-buffer nil + python-pdbtrack-buffers-to-kill nil))))) + output) + +;;; + +(defun python-shell-completion--get-completions (input process completion-code) + "Retrieve available completions for INPUT using PROCESS. +Argument COMPLETION-CODE is the python code used to get +completions on the current context." + (with-current-buffer (process-buffer process) + (let ((completions + (python-shell-send-string-no-output + (format completion-code input) process))) + (when (> (length completions) 2) + (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) + +(defun python-shell-completion--do-completion-at-point (process imports input) + "Do completion at point for PROCESS." + (with-syntax-table python-dotty-syntax-table + (let* ((code + (if imports + (concat imports python-shell-module-completion-string-code) + python-shell-module-completion-string-code)) + (completions + (python-shell-completion--get-completions + input process code)) + (completion (when completions + (try-completion input completions)))) + (cond ((eq completion t) + (if (eq this-command last-command) + (when python-completion-original-window-configuration + (set-window-configuration + python-completion-original-window-configuration))) + (setq python-completion-original-window-configuration nil) + nil) + ((null completion) + (message "Can't find completion for \"%s\"" input) + (ding) + nil) + ((not (string= input completion)) + (progn (delete-char (- (length input))) + (insert completion) + ;; minibuffer.el expects a list, a bug IMO + nil)) + (t + (unless python-completion-original-window-configuration + (setq python-completion-original-window-configuration + (current-window-configuration))) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions))) + nil))))) + +(defun python-shell-completion-complete-at-point () + "Perform completion at point in inferior Python process." + (interactive) + (and comint-last-prompt-overlay + (> (point-marker) (overlay-end comint-last-prompt-overlay)) + (python-shell-completion--do-completion-at-point + (get-buffer-process (current-buffer))(buffer-substring-no-properties beg end) word))) + +(defun python-shell-completion-complete-or-indent () + "Complete or indent depending on the context. +If content before pointer is all whitespace indent. If not try +to complete." + (interactive) + (if (string-match "^[[:space:]]*$" + (buffer-substring (comint-line-beginning-position) + (point-marker))) + (indent-for-tab-command) + (comint-dynamic-complete))) + +;;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT +(defvar ipython-version nil) + +(defvar python-command "python" + "Used by `py-completion-at-point', derived from python.el" ) + +(defvaralias 'python-python-command-args 'py-python-command-args) +(defvaralias 'py-python-command 'py-shell-name) +(defvaralias 'py-jpython-command 'py-shell-name) +(defvaralias 'py-jython-command 'py-shell-name) +(defvaralias 'py-default-interpreter 'py-shell-name) +;; (defvaralias 'python-command 'py-shell-name) + +(defvar py-shell-template " +\(defun NAME (&optional argprompt) + \"Start an DOCNAME interpreter in another window. + +With optional \\\\[universal-argument] user is prompted +for options to pass to the DOCNAME interpreter. \" + (interactive \"P\") + (let\* ((py-shell-name \"FULLNAME\")) + (py-shell argprompt) + (when (interactive-p) (switch-to-buffer (current-buffer)) + (goto-char (point-max))))) +") + +(setq py-shell-template " +\(defun NAME (&optional argprompt) + \"Start an DOCNAME interpreter in another window. + +With optional \\\\[universal-argument] user is prompted +for options to pass to the DOCNAME interpreter. \" + (interactive \"P\") + (let\* ((py-shell-name \"FULLNAME\")) + (py-shell argprompt) + (when (interactive-p) (switch-to-buffer (current-buffer)) + (goto-char (point-max))))) +") + +(defvar view-return-to-alist) +(defvar python-imports) ; forward declaration + +(defvar py-execute-directory nil + "Stores the file's directory-name py-execute-... functions act upon. ") + +(defvar py-prev-dir/file nil + "Caches (directory . file) pair used in the last `py-load-file' command. +Used for determining the default in the next one.") + +(defvar py-exception-buffer nil) + +(defvar py-output-buffer "*Python Output*") +(make-variable-buffer-local 'py-output-buffer) + +(defvar py-execute-keep-temporary-file-p nil + "For tests only. Excute functions delete temporary files default. ") + +(defvar py-expression-skip-regexp "^ =:#\t\r\n\f" + "py-expression assumes chars indicated possible composing a py-expression, skip it. ") +;; (setq py-expression-skip-regexp "^ =:#\t\r\n\f") + +(defvar py-expression-looking-regexp "[^ =:#\t\r\n\f]+" + "py-expression assumes chars indicated possible composing a py-expression, when looking-at or -back. ") +;; (setq py-expression-looking-regexp "[^ =:#\t\r\n\f)]") + +(defvar py-not-expression-regexp "[ .=:#\t\r\n\f)]" + "py-expression assumes chars indicated probably will not compose a py-expression. ") +;; (setq py-not-expression-regexp "[ .=:#\t\r\n\f)]") + +(defvar py-partial-expression-skip-regexp "^ .()[]{}=:#\t\r\n\f" + "py-partial-expression assumes chars indicated possible composing a py-partial-expression, skip it. ") +;; (setq py-partial-expression-skip-regexp "^ .(){}=:#\t\r\n\f") + +(defvar py-partial-expression-forward-regexp "^ .)}=:#\t\r\n\f" + "py-partial-expression assumes chars indicated possible composing a py-partial-expression, skip it. ") + +(defvar py-partial-expression-backward-regexp "^ .({=:#\t\r\n\f" + "py-partial-expression assumes chars indicated possible composing a py-partial-expression, skip it. ") + +(defvar py-not-partial-expression-skip-regexp " \\.=:#\t\r\n\f" + "py-partial-expression assumes chars indicated may not compose a py-partial-expression, skip it. ") + +(defvar py-partial-expression-looking-regexp "[^ .=:#\t\r\n\f]" + "py-partial-expression assumes chars indicated possible composing a py-partial-expression, when looking-at or -back. ") +;; (setq py-partial-expression-looking-regexp "[^ .=:#\t\r\n\f)]") + +(defvar py-not-partial-expression-regexp "[ .=:#\t\r\n\f)]" + "py-partial-expression assumes chars indicated probably will not compose a py-partial-expression. ") +;; (setq py-not-partial-expression-regexp "[ .=:#\t\r\n\f)]") + +(defvar py-line-number-offset 0 + "When an exception occurs as a result of py-execute-region, a +subsequent py-up-exception needs the line number where the region +started, in order to jump to the correct file line. This variable is +set in py-execute-region and used in py-jump-to-exception.") + +(defvar match-paren-no-use-syntax-pps nil) + +(defsubst py-keep-region-active () + "Keep the region active in XEmacs." + ;; Ignore byte-compiler warnings you might see. Also note that + ;; FSF's Emacs does it differently; its policy doesn't require us + ;; to take explicit action. + (and (boundp 'zmacs-region-stays) + (setq zmacs-region-stays t))) + +;; Constants +(defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)" + "Regular expression matching a blank or comment line.") + +(defconst py-block-closing-keywords-re + "[ \t]*\\<\\(return\\|raise\\|break\\|continue\\|pass\\)\\_>[ \n\t]" + "Matches the beginning of a class, method or compound statement. ") + +(defconst py-finally-re + "[ \t]*\\_<finally\\_>[: \n\t]" + "Regular expression matching keyword which closes a try-block. ") + +(defconst py-except-re + "[ \t]*\\_<except\\_>[: \n\t]" + "Regular expression matching keyword which composes a try-block. ") + +(defconst py-else-re + "[ \t]*\\_<else\\_>[: \n\t]" + "Regular expression matching keyword which closes a for- if- or try-block. ") + +(defconst py-return-re + ".*:?[ \t]*\\_<\\(return\\)\\_>[ \n\t]" + "Regular expression matching keyword which typically closes a function. ") + +(defconst py-no-outdent-re "\\(try:\\|except\\(\\s +.*\\)?:\\|while\\s +.*:\\|for\\s +.*:\\|if\\s +.*:\\|elif\\s +.*:\\)\\([ ]*\\_<\\(return\\|raise\\|break\\|continue\\|pass\\)\\_>[ \n]\\)") + +;; (defconst py-no-outdent-re +;; (concat +;; "\\(" +;; (mapconcat 'identity +;; (list "try:" +;; "except\\(\\s +.*\\)?:" +;; "while\\s +.*:" +;; "for\\s +.*:" +;; "if\\s +.*:" +;; "elif\\s +.*:" +;; (concat py-block-closing-keywords-re "[ \t\n]")) +;; "\\|") +;; "\\)") +;; "Regular expression matching lines not to dedent after.") + +(defvar py-traceback-line-re + "^IPython\\|^In \\[[0-9]+\\]: *\\|^>>>\\|^[^ \t>]+>[^0-9]+\\([0-9]+\\)\\|^[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\)" + "Regular expression that describes tracebacks. +Inludes Python shell-prompt in order to stop further searches. ") + +(defconst py-assignment-re "\\<\\w+\\>[ \t]*\\(=\\|+=\\|*=\\|%=\\|&=\\|^=\\|<<=\\|-=\\|/=\\|**=\\||=\\|>>=\\|//=\\)" + "If looking at the beginning of an assignment. ") + +(defconst py-block-re "[ \t]*\\_<\\(class\\|def\\|for\\|if\\|try\\|while\\|with\\)\\_>[: \n\t]" + "Matches the beginning of a compound statement. ") + +(defconst py-minor-block-re "[ \t]*\\_<\\(for\\|if\\|try\\)\\_>[: \n\t]" + "Matches the beginning of an `for', `if' or `try' block. ") + +(defconst py-try-block-re "[ \t]*\\_<try\\_>[: \n\t]" + "Matches the beginning of an `if' or `try' block. ") + +(defconst py-class-re "[ \t]*\\_<\\(class\\)\\_>[ \n\t]" + "Matches the beginning of a class definition. ") + +(defconst py-def-or-class-re "[ \t]*\\_<\\(def\\|class\\)\\_>[ \n\t]" + "Matches the beginning of a class- or functions definition. ") + +(defconst py-def-re "[ \t]*\\_<\\(def\\)\\_>[ \n\t]" + "Matches the beginning of a functions definition. ") + +(defconst py-block-or-clause-re "[ \t]*\\_<\\(if\\|else\\|elif\\|while\\|for\\|def\\|class\\|try\\|except\\|finally\\|with\\)\\_>[: \n\t]" + "Matches the beginning of a compound statement or it's clause. ") + +(defconst py-clause-re "[ \t]*\\_<\\(else\\|elif\\|except\\|finally\\)\\_>[: \n\t]" + "Matches the beginning of a compound statement's clause. ") + +(defconst py-elif-re "[ \t]*\\_<\\elif\\_>[: \n\t]" + "Matches the beginning of a compound if-statement's clause exclusively. ") + +(defconst py-try-clause-re "[ \t]*\\_<\\(except\\|else\\|finally\\)\\_>[: \n\t]" + "Matches the beginning of a compound try-statement's clause. ") + +(defconst py-if-re "[ \t]*\\_<if\\_>[ \n\t]" + "Matches the beginning of a compound statement saying `if'. ") + +(defconst py-try-re "[ \t]*\\_<try\\_>[: \n\t]" + "Matches the beginning of a compound statement saying `try'. " ) + +;;; Macro definitions +(defmacro py-in-string-or-comment-p () + "Returns beginning position if inside a string or comment, nil otherwise. " + `(or (nth 8 (syntax-ppss)) + (when (or (looking-at "\"")(looking-at "[ \t]*#[ \t]*")) + (match-beginning 0)))) + +(defmacro empty-line-p () + "Returns t if cursor is at an line with nothing but whitespace-characters, nil otherwise." + (interactive "p") + `(save-excursion + (progn + (beginning-of-line) + (looking-at "\\s-*$")))) + +(defmacro py-escaped () + "Return t if char is preceded by an odd number of backslashes. " + `(save-excursion + (< 0 (% (abs (skip-chars-backward "\\\\")) 2)))) + +(defmacro py-preceding-line-backslashed-p () + "Return t if preceding line is a backslashed continuation line. " + `(save-excursion + (beginning-of-line) + (skip-chars-backward " \t\r\n\f") + (and (eq (char-before (point)) ?\\ ) + (py-escaped)))) + +(defmacro py-current-line-backslashed-p () + "Return t if current line is a backslashed continuation line. " + `(save-excursion + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (and (eq (char-before (point)) ?\\ ) + (py-escaped)))) + +(defmacro py-continuation-line-p () + "Return t iff current line is a continuation line." + `(save-excursion + (beginning-of-line) + (or (py-preceding-line-backslashed-p) + (< 0 (nth 0 (syntax-ppss)))))) + +(defun py-count-lines (&optional start end) + "Count lines in accessible part until current line. + +See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7115" + (interactive) + (save-excursion + (let ((count 0) + (orig (point))) + (goto-char (point-min)) + (while (and (< (point) orig)(not (eobp)) (skip-chars-forward "^\n" orig)) + (setq count (1+ count)) + (unless (or (not (< (point) orig)) (eobp)) (forward-char 1) + (setq count (+ count (abs (skip-chars-forward "\n" orig)))))) + (when (bolp) (setq count (1+ count))) + (when (interactive-p) (message "%s" count)) + count))) + + +(eval-when-compile + (defconst python-rx-constituents + (list + `(block-start . ,(rx symbol-start + (or "def" "class" "if" "elif" "else" "try" + "except" "finally" "for" "while" "with") + symbol-end)) + `(decorator . ,(rx line-start (* space) ?@ (any letter ?_) + (* (any word ?_)))) + `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) + `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) + `(open-paren . ,(rx (or "{" "[" "("))) + `(close-paren . ,(rx (or "}" "]" ")"))) + `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) + `(not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) + `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" + "=" "%" "**" "//" "<<" ">>" "<=" "!=" + "==" ">=" "is" "not"))) + `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" + ">>=" "<<=" "&=" "^=" "|=")))) + "Additional Python specific sexps for `python-rx'")) + +(defmacro python-rx (&rest regexps) + "Python mode specialized rx macro which supports common python named REGEXPS." + (let ((rx-constituents (append python-rx-constituents rx-constituents))) + (cond ((null regexps) + (error "No regexp")) + ((cdr regexps) + (rx-to-string `(and ,@regexps) t)) + (t + (rx-to-string (car regexps) t))))) + +;;; +;; GNU's syntax-ppss-context +(unless (functionp 'syntax-ppss-context) + (defsubst syntax-ppss-context (ppss) + (cond + ((nth 3 ppss) 'string) + ((nth 4 ppss) 'comment) + (t nil)))) + +;; Skip's XE workaround +(unless (fboundp 'string-to-syntax) + (defun string-to-syntax (s) + (cond + ((equal s "|") '(15)) + ((equal s "_") '(3)) + (t (error "Unhandled string: %s" s))))) + +;;; GNU python.el stuff + +(defun py-history-input-filter (str) + "`comint-input-filter' function for inferior Python. +Don't save anything for STR matching `py-history-filter-regexp'." + (not (string-match py-history-filter-regexp str))) + +;; Fixme: Loses with quoted whitespace. +(defun python-args-to-list (string) + (let ((where (string-match "[ \t]" string))) + (cond ((null where) (list string)) + ((not (= where 0)) + (cons (substring string 0 where) + (python-args-to-list (substring string (+ 1 where))))) + (t (let ((pos (string-match "[^ \t]" string))) + (if pos (python-args-to-list (substring string pos)))))))) + +(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 + (setq s (concat python-preoutput-leftover s)) + (setq python-preoutput-leftover nil)) + (let ((start 0) + (res "")) + ;; First process whole lines. + (while (string-match "\n" s start) + (let ((line (substring s start (setq start (match-end 0))))) + ;; Skip prompt if needed. + (when (and python-preoutput-skip-next-prompt + (string-match comint-prompt-regexp line)) + (setq python-preoutput-skip-next-prompt nil) + (setq line (substring line (match-end 0)))) + ;; Recognize special _emacs_out lines. + (if (and (string-match "\\`_emacs_out \\(.*\\)\n\\'" line) + (local-variable-p 'python-preoutput-result)) + (progn + (setq python-preoutput-result (match-string 1 line)) + (set (make-local-variable 'python-preoutput-skip-next-prompt) t)) + (setq res (concat res line))))) + ;; Then process the remaining partial line. + (unless (zerop start) (setq s (substring s start))) + (cond ((and (string-match comint-prompt-regexp s) + ;; Drop this prompt if it follows an _emacs_out... + (or python-preoutput-skip-next-prompt + ;; ... or if it's not gonna be inserted at BOL. + ;; Maybe we could be more selective here. + (if (zerop (length res)) + (not (bolp)) + (string-match ".\\'" res)))) + ;; The need for this seems to be system-dependent: + ;; What is this all about, exactly? --Stef + ;; (if (and (eq ?. (aref s 0))) + ;; (accept-process-output (get-buffer-process (current-buffer)) 1)) + (setq python-preoutput-skip-next-prompt nil) + res) + ((let ((end (min (length "_emacs_out ") (length s)))) + (eq t (compare-strings s nil end "_emacs_out " nil end))) + ;; The leftover string is a prefix of _emacs_out so we don't know + ;; yet whether it's an _emacs_out or something else: wait until we + ;; get more output so we can resolve this ambiguity. + (set (make-local-variable 'python-preoutput-leftover) s) + res) + (t (concat res s))))) + +(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 + " -c \"from sys import version_info;\ +print version_info >= (2, 2) and version_info < (3, 0)\"")))) + (unless (string-match "True" res) + (error "Only Python versions >= 2.2 and < 3.0 are supported"))) + (setq python-version-checked t))) + +(defun run-python (&optional cmd noshow new) + "Run an inferior Python process, input and output via buffer *Python*. + +CMD is the Python command to run. NOSHOW non-nil means don't +show the buffer automatically. + +Interactively, a prefix arg means to prompt for the initial +Python command line (default is `python-command'). + +A new process is started if one isn't running attached to +`python-buffer', or if called from Lisp with non-nil arg NEW. +Otherwise, if a process is already running in `python-buffer', +switch to that buffer. + +This command runs the hook `inferior-python-mode-hook' after +running `comint-mode-hook'. Type \\[describe-mode] in the +process buffer for a list of commands. + +By default, Emacs inhibits the loading of Python modules from the +current working directory, for security reasons. To disable this +behavior, change `python-remove-cwd-from-path' to nil." + (interactive (if current-prefix-arg + (list (read-string "Run Python: " python-command) nil t) + (list python-command))) + (unless cmd (setq cmd 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. + (when (or new (not (comint-check-proc python-buffer))) + (with-current-buffer + (let* ((cmdlist + (append (python-args-to-list cmd) '("-i") + (if python-remove-cwd-from-path + '("-c" "import sys; sys.path.remove('')")))) + (path (getenv "PYTHONPATH")) + (process-environment ; to import emacs.py + (cons (concat "PYTHONPATH=" + (if path (concat path path-separator)) + data-directory) + process-environment)) + ;; If we use a pipe, unicode characters are not printed + ;; correctly (Bug#5794) and IPython does not work at + ;; all (Bug#5390). + ;; (process-connection-type t)) + ) + (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) + (inferior-python-mode) + ;; Load function definitions 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. The code might be inline here, but there's enough that it + ;; 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. + (py-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 + ;; the process isn't already running. + (sit-for 1 t) ;Should we use accept-process-output instead? --Stef + (unless noshow (pop-to-buffer python-buffer t))) + +(defun python-send-command (command) + "Like `python-send-string' but resets `compilation-shell-minor-mode'." + (when (python-check-comint-prompt) + (with-current-buffer (process-buffer (py-proc)) + (goto-char (point-max)) + (compilation-forget-errors) + (py-send-string command) + (setq compilation-last-buffer (current-buffer))))) + +(defun python-send-region (start end) + "Send the region to the inferior Python process." + ;; The region is evaluated from a temporary file. This avoids + ;; problems with blank lines, which have different semantics + ;; interactively and in files. It also saves the inferior process + ;; buffer filling up with interpreter prompts. We need a Python + ;; function to remove the temporary file when it has been evaluated + ;; (though we could probably do it in Lisp with a Comint output + ;; filter). This function also catches exceptions and truncates + ;; tracebacks not to mention the frame of the function itself. + ;; + ;; The `compilation-shell-minor-mode' parsing takes care of relating + ;; the reference to the temporary file to the source. + ;; + ;; Fixme: Write a `coding' header to the temp file if the region is + ;; non-ASCII. + (interactive "r") + (let* ((f (make-temp-file "py")) + (command + ;; IPython puts the FakeModule module into __main__ so + ;; emacs.eexecfile becomes useless. + (if (or (string-match "[iI][pP]ython[^[:alpha:]]*$" (py-choose-shell)) + (string-match "[pP]ython3[[:alnum:]:]*$" (py-choose-shell))) + (format "execfile %S" f) + (format "emacs.eexecfile(%S)" f))) + (orig-start (copy-marker start))) + (when (save-excursion + (goto-char start) + (/= 0 (current-indentation))) ; need dummy block + (save-excursion + (goto-char orig-start) + ;; Wrong if we had indented code at buffer start. + (set-marker orig-start (line-beginning-position 0))) + (write-region "if True:\n" nil f nil 'nomsg)) + (write-region start end f t 'nomsg) + (python-send-command command) + (with-current-buffer (process-buffer (py-proc)) + ;; Tell compile.el to redirect error locations in file `f' to + ;; positions past marker `orig-start'. It has to be done *after* + ;; `python-send-command''s call to `compilation-forget-errors'. + (compilation-fake-loc orig-start f)))) + +(defun python-send-string (string) + "Evaluate STRING in inferior Python process." + (interactive "sPython command: ") + (comint-send-string (py-proc) string) + (unless (string-match "\n\\'" string) + ;; Make sure the text is properly LF-terminated. + (comint-send-string (py-proc) "\n")) + (when (string-match "\n[ \t].*\n?\\'" string) + ;; If the string contains a final indented line, add a second newline so + ;; as to make sure we terminate the multiline instruction. + (comint-send-string (py-proc) "\n"))) + +(defun python-send-buffer () + "Send the current buffer to the inferior Python process." + (interactive) + (python-send-region (point-min) (point-max))) + +;; Fixme: Try to define the function or class within the relevant +;; module, not just at top level. +(defun python-send-defun () + "Send the current defun (class or method) to the inferior Python process." + (interactive) + (save-excursion (python-send-region (progn (beginning-of-defun) (point)) + (progn (end-of-defun) (point))))) + +(defun python-switch-to-python (eob-p) + "Switch to the Python process buffer, maybe starting new process. +With prefix arg, position cursor at end of buffer." + (interactive "P") + (pop-to-buffer (process-buffer (py-proc)) t) ;Runs python if needed. + (when eob-p + (push-mark) + (goto-char (point-max)))) + +(defun python-send-region-and-go (start end) + "Send the region to the inferior Python process. +Then switch to the process buffer." + (interactive "r") + (python-send-region start end) + (python-switch-to-python t)) + +(defvar python-prev-dir/file nil + "Caches (directory . file) pair used in the last `python-load-file' command. +Used for determining the default in the next one.") + +(defun python-load-file (file-name) + "Load a Python file FILE-NAME into the inferior Python process. +If the file has extension `.py' import or reload it as a module. +Treating it as a module keeps the global namespace clean, provides +function location information for debugging, and supports users of +module-qualified names." + (interactive (comint-get-source "Load Python file: " python-prev-dir/file + python-source-modes + t)) ; because execfile needs exact name + (comint-check-source file-name) ; Check to see if buffer needs saving. + (setq python-prev-dir/file (cons (file-name-directory file-name) + (file-name-nondirectory file-name))) + (with-current-buffer (process-buffer (py-proc)) ;Runs python if needed. + ;; Fixme: I'm not convinced by this logic from python-mode.el. + (python-send-command + (if (string-match "\\.py\\'" file-name) + (let ((module (file-name-sans-extension + (file-name-nondirectory file-name)))) + (format "emacs.eimport(%S,%S)" + module (file-name-directory file-name))) + (format "execfile(%S)" file-name))) + (message "%s loaded" file-name))) + +(defun py-proc () + "Return the current Python process. + +See variable `python-buffer'. Starts a new process if necessary." + ;; Fixme: Maybe should look for another active process if there + ;; isn't one for `python-buffer'. + (unless (comint-check-proc python-buffer) + (run-python nil t)) + (get-buffer-process (if (derived-mode-p 'inferior-python-mode) + (current-buffer) + python-buffer))) + +(defun python-set-proc () + "Set the default value of `python-buffer' to correspond to this buffer. +If the current buffer has a local value of `python-buffer', set the +default (global) value to that. The associated Python process is +the one that gets input from \\[python-send-region] et al when used +in a buffer that doesn't have a local value of `python-buffer'." + (interactive) + (if (local-variable-p 'python-buffer) + (setq-default python-buffer python-buffer) + (error "No local value of `python-buffer'"))) + +;; Fixme: Should this actually be used instead of info-look, i.e. be +;; bound to C-h S? [Probably not, since info-look may work in cases +;; where this doesn't.] +;; (defun python-describe-symbol (symbol) +;; "Get help on SYMBOL using `help'. +;; Interactively, prompt for symbol. +;; +;; Symbol may be anything recognized by the interpreter's `help' +;; command -- e.g. `CALLS' -- not just variables in scope in the +;; interpreter. This only works for Python version 2.2 or newer +;; since earlier interpreters don't support `help'. +;; +;; In some cases where this doesn't find documentation, \\[info-lookup-symbol] +;; will." +;; ;; Note that we do this in the inferior process, not a separate one, to +;; ;; ensure the environment is appropriate. +;; (interactive +;; (let ((symbol (with-syntax-table python-dotty-syntax-table +;; (current-word))) +;; (enable-recursive-minibuffers t)) +;; (list (read-string (if symbol +;; (format "Describe symbol (default %s): " symbol) +;; "Describe symbol: ") +;; nil nil symbol)))) +;; (if (equal symbol "") (error "No symbol")) +;; ;; Ensure we have a suitable help buffer. +;; ;; Fixme: Maybe process `Related help topics' a la help xrefs and +;; ;; allow C-c C-f in help buffer. +;; (let ((temp-buffer-show-hook ; avoid xref stuff +;; (lambda () +;; (toggle-read-only 1) +;; (setq view-return-to-alist +;; (list (cons (selected-window) help-return-method)))))) +;; (with-output-to-temp-buffer (help-buffer) +;; (with-current-buffer standard-output +;; ;; Fixme: Is this actually useful? +;; (help-setup-xref (list 'python-describe-symbol symbol) +;; (called-interactively-p 'interactive)) +;; (set (make-local-variable 'comint-redirect-subvert-readonly) t) +;; (help-print-return-message)))) +;; (comint-redirect-send-command-to-process (format "emacs.ehelp(%S, %s)" +;; symbol python-imports) +;; "*Help*" (py-proc) nil nil)) + +(add-to-list 'debug-ignored-errors "^No symbol") + +(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. +This is a no-op if `python-check-comint-prompt' returns nil." + (py-send-string string) + (let ((proc (py-proc))) + (with-current-buffer (process-buffer proc) + (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 if and only if 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 (py-proc))) + (save-excursion + (save-match-data + (re-search-backward (concat python--prompt-regexp " *\\=") + nil t))))) + +;; Fixme: Is there anything reasonable we can do with random methods? +;; (Currently only works with functions.) +(defun python-eldoc-function () + "`eldoc-documentation-function' for Python. +Only works when point is in a function name, not its arg list, for +instance. Assumes an inferior Python is running." + (let ((symbol (with-syntax-table python-dotty-syntax-table + (current-word)))) + ;; This is run from timers, so inhibit-quit tends to be set. + (with-local-quit + ;; First try the symbol we're on. + (or (and symbol + (py-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. + +(declare-function info-lookup-maybe-add-help "info-look" (&rest arg)) + +(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 py-shell-name + " -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 + (with-no-warnings (Info-mode)) + (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) + (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)) + +;;;; Miscellany. + +;; Called from `python-mode', this causes a recursive call of the +;; mode. See logic there to break out of the recursion. +(defun python-maybe-jython () + "Invoke `jython-mode' if the buffer appears to contain Jython code. +The criterion is either a match for `jython-mode' via +`interpreter-mode-alist' or an import of a module from the list +`python-jython-packages'." + ;; The logic is taken from python-mode.el. + (save-excursion + (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 bol (or "import" "from") (1+ space) + (group (1+ (not (any " \t\n."))))) + (+ (point-min) 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) + "`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 string's indentation." + (interactive "P") + (or (fill-comment-paragraph justify) + (save-excursion + (end-of-line) + (let* ((syntax (syntax-ppss)) + (orig (point)) + start end) + (cond ((nth 4 syntax) ; comment. fixme: loses with trailing one + (let (fill-paragraph-function) + (fill-paragraph justify))) + ;; 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. + ((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 + (progn (forward-sexp) + (- (point) 3)) + (error (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 (line-beginning-position))) + (error nil)))) + (when end + (save-restriction + (narrow-to-region start end) + (goto-char orig) + ;; Avoid losing leading and trailing newlines in doc + ;; strings written like: + ;; """ + ;; ... + ;; """ + (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) + "Shift lines in region COUNT (the prefix arg) columns to the left. +COUNT defaults to `py-indent-offset'. If region isn't active, just shift +current line. The region shifted includes the lines in which START and +END lie. It is an error if any lines in the region are indented less than +COUNT columns." + (interactive + (if mark-active + (list (region-beginning) (region-end) current-prefix-arg) + (list (line-beginning-position) (line-end-position) current-prefix-arg))) + (if count + (setq count (prefix-numeric-value count)) + (setq count py-indent-offset)) + (when (> count 0) + (save-excursion + (goto-char start) + (while (< (point) end) + (if (and (< (current-indentation) count) + (not (looking-at "[ \t]*$"))) + (error "Can't shift all lines enough")) + (forward-line)) + (indent-rigidly start end (- count))))) + +(add-to-list 'debug-ignored-errors "^Can't shift all lines enough") + +(defun python-shift-right (start end &optional count) + "Shift lines in region COUNT (the prefix arg) columns to the right. +COUNT defaults to `py-indent-offset'. If region isn't active, just shift +current line. The region shifted includes the lines in which START and +END lie." + (interactive + (if mark-active + (list (region-beginning) (region-end) current-prefix-arg) + (list (line-beginning-position) (line-end-position) current-prefix-arg))) + (if count + (setq count (prefix-numeric-value count)) + (setq count py-indent-offset)) + (indent-rigidly start end count)) + +(defun python-outline-level () + "`outline-level' function for Python mode. +The level is the number of `py-indent-offset' steps of indentation +of current line." + (1+ (/ (current-indentation) py-indent-offset))) + +;; Fixme: Consider top-level assignments, imports, &c. +(defun python-current-defun (&optional length-limit) + "`add-log-current-defun-function' for Python." + (save-excursion + ;; Move up the tree of nested `class' and `def' blocks until we + ;; get to zero indentation, accumulating the defined names. + (let ((accum) + (length -1)) + (catch 'done + (while (or (null length-limit) + (null (cdr accum)) + (< length length-limit)) + (let ((started-from (point))) + (python-beginning-of-block) + (end-of-line) + (beginning-of-defun) + (when (= (point) started-from) + (throw 'done nil))) + (when (looking-at (rx (0+ space) (or "def" "class") (1+ space) + (group (1+ (or word (syntax symbol)))))) + (push (match-string 1) accum) + (setq length (+ length 1 (length (car accum))))) + (when (= (current-indentation) 0) + (throw 'done nil)))) + (when accum + (when (and length-limit (> length length-limit)) + (setcar accum "..")) + (mapconcat 'identity accum "."))))) + +(defun python-mark-block () + "Mark the block around point. +Uses `python-beginning-of-block', `python-end-of-block'." + (interactive) + (push-mark) + (python-beginning-of-block) + (push-mark (point) nil t) + (python-end-of-block) + (exchange-point-and-mark)) + +;; Fixme: Provide a find-function-like command to find source of a +;; definition (separate from BicycleRepairMan). Complicated by +;; finding the right qualified name. + +;;;; Completion. + +;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2008-01/msg00076.html +(defvar python-imports "None" + "String of top-level import statements updated by `python-find-imports'.") +(make-variable-buffer-local 'python-imports) + +;; Fixme: Should font-lock try to run this when it deals with an import? +;; Maybe not a good idea if it gets run multiple times when the +;; statement is being edited, and is more likely to end up with +;; something syntactically incorrect. +;; However, what we should do is to trundle up the block tree from point +;; to extract imports that appear to be in scope, and add those. +(defun python-find-imports () + "Find top-level imports, updating `python-imports'." + (interactive) + (save-excursion + (let (lines) + (goto-char (point-min)) + (while (re-search-forward "^import\\_>\\|^from\\_>" nil t) + (unless (syntax-ppss-context (syntax-ppss)) + (let ((start (line-beginning-position))) + ;; Skip over continued lines. + (while (and (eq ?\\ (char-before (line-end-position))) + (= 0 (forward-line 1))) + t) + (push (buffer-substring start (line-beginning-position 2)) + lines)))) + (setq python-imports + (if lines + (apply #'concat + ;; This is probably best left out since you're unlikely to need the + ;; doc for a function in the buffer and the import will lose if the + ;; Python sub-process' working directory isn't the same as the + ;; buffer's. + ;; (if buffer-file-name + ;; (concat + ;; "import " + ;; (file-name-sans-extension + ;; (file-name-nondirectory buffer-file-name)))) + (nreverse lines)) + "None")) + (when lines + (set-text-properties 0 (length python-imports) nil python-imports) + ;; The output ends up in the wrong place if the string we + ;; send contains newlines (from the imports). + (setq python-imports + (replace-regexp-in-string "\n" "\\n" + (format "%S" python-imports) t t)))))) + +(defun python-completion-at-point () + (let ((end (point)) + (start (save-excursion + (and (re-search-backward + (rx (or buffer-start (regexp "[^[:alnum:]._]")) + (group (1+ (regexp "[[:alnum:]._]"))) point) + nil t) + (match-beginning 1))))) + (when start + (list start end + (completion-table-dynamic 'python-symbol-completions))))) + +;;; FFAP support + +(defun python-module-path (module) + "Function for `ffap-alist' to return path to MODULE." + (python-send-receive (format "emacs.modpath (%S)" module))) + +(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-char (point-min)) + (forward-line (1- line))))) + +;;; +(defvar py-mode-syntax-table nil) +(setq py-mode-syntax-table + (let ((table (make-syntax-table)) + (tablelookup (if (featurep 'xemacs) + 'get-char-table + 'aref))) + ;; Give punctuation syntax to ASCII that normally has symbol + ;; syntax or has word syntax and isn't a letter. + (if (featurep 'xemacs) + (setq table (standard-syntax-table)) + (let ((symbol (string-to-syntax "_")) + ;; (symbol (string-to-syntax "_")) + (sst (standard-syntax-table))) + (dotimes (i 128) + (unless (= i ?_) + (if (equal symbol (funcall tablelookup sst i)) + (modify-syntax-entry i "." table)))))) + (modify-syntax-entry ?$ "." table) + (modify-syntax-entry ?% "." table) + ;; exceptions + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?' "\"" table) + (modify-syntax-entry ?` "$" table) + (modify-syntax-entry ?\_ "w" table) + table)) + +(defvar py-help-mode-syntax-table + (let ((st (make-syntax-table py-mode-syntax-table))) + ;; Don't get confused by apostrophes in the process's output (e.g. if + ;; you execute "help(os)"). + (modify-syntax-entry ?\' "." st) + ;; Maybe we should do the same for double quotes? + (modify-syntax-entry ?\" "." st) + st)) + +(defconst py-space-backslash-table + (let ((table (copy-syntax-table py-mode-syntax-table))) + (modify-syntax-entry ?\\ " " table) + table) + "`py-mode-syntax-table' with backslash given whitespace syntax.") + +;; have to bind py-file-queue before installing the kill-emacs-hook +(defvar py-file-queue nil + "Queue of Python temp files awaiting execution. +Currently-active file is at the head of the list.") + +(defvar python-mode-abbrev-table nil) +(define-abbrev-table 'python-mode-abbrev-table ()) + +(defvar inferior-python-mode-abbrev-table nil + "Not in use.") +(define-abbrev-table 'inferior-python-mode-abbrev-table ()) + +;; pdbtrack constants +(defconst py-pdbtrack-stack-entry-regexp + (concat ".*\\("py-shell-input-prompt-1-regexp">\\|>\\) *\\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>()]+\\)()") + "Regular expression pdbtrack uses to find a stack trace entry.") + +;; ipython.el +;; Recognize the ipython pdb, whose prompt is 'ipdb>' or 'ipydb>' +;;instead of '(Pdb)' +(defvar py-pdbtrack-input-prompt) +(setq py-pdbtrack-input-prompt "^[(<]*[Ii]?[Pp]y?db[>)]+ ") +(defvar py-pydbtrack-input-prompt) +(setq py-pydbtrack-input-prompt "^[(]*ipydb[>)]+ ") + +;; pydb-328837.diff +;; (defconst py-pydbtrack-stack-entry-regexp +;; "^(\\([-a-zA-Z0-9_/.]*\\):\\([0-9]+\\)):[ \t]?\\(.*\n\\)" +;; "Regular expression pdbtrack uses to find a stack trace entry for pydb. +;; +;; The debugger outputs program-location lines that look like this: +;; (/usr/bin/zonetab2pot.py:15): makePOT") + +(defconst py-pdbtrack-marker-regexp-file-group 2 + "Group position in gud-pydb-marker-regexp that matches the file name.") + +(defconst py-pdbtrack-marker-regexp-line-group 3 + "Group position in gud-pydb-marker-regexp that matches the line number.") + +(defconst py-pdbtrack-marker-regexp-funcname-group 4 + "Group position in gud-pydb-marker-regexp that matches the function name.") + +(defconst py-pdbtrack-track-range 10000 + "Max number of characters from end of buffer to search for stack entry.") + +(defvar py-pdbtrack-is-tracking-p nil) + +;;; Bindings +(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'") 'python-mode)) +(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode)) +(add-to-list 'interpreter-mode-alist (cons (purecopy "jython") 'jython-mode)) +(add-to-list 'same-window-buffer-names (purecopy "*Python*")) + +(defconst python-font-lock-syntactic-keywords + ;; Make outer chars of matching triple-quote sequences into generic + ;; string delimiters. Fixme: Is there a better way? + ;; First avoid a sequence preceded by an odd number of backslashes. + `((,(concat "\\(?:^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + "\\(?1:\"\\)\\(?2:\"\\)\\(?3:\"\\)\\(?4:\"\\)\\(?5:\"\\)\\(?6:\"\\)\\|\\(?1:\"\\)\\(?2:\"\\)\\(?3:\"\\)\\|\\('\\)\\('\\)\\('\\)\\|\\('\\)\\('\\)\\('\\)\\('\\)\\('\\)\\('\\)") + (1 (python-quote-syntax 1) t t) + (2 (python-quote-syntax 2) t t) + (3 (python-quote-syntax 3) t t) + (6 (python-quote-syntax 1) t t)) + ;; This doesn't really help. + ;; (,(rx (and ?\\ (group ?\n))) (1 " ")) + )) + +(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 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 """ \"""" x + (save-excursion + (goto-char (match-beginning 0)) + (cond + ;; Consider property for the last char if in a fenced string. + ((= n 3) + (let* ((font-lock-syntactic-keywords nil) + (syntax (syntax-ppss))) + (when (eq t (nth 3 syntax)) ; after unclosed fence + (goto-char (nth 8 syntax)) ; fence position + ;; (skip-chars-forward "uUrR") ; skip any prefix + ;; 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) ; leading quote (not prefix) + (not (match-end 1))) ; prefix is null + (and (= n 1) ; prefix + (match-end 1))) ; non-empty + (let ((font-lock-syntactic-keywords nil)) + (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. + ))) + + +;;; Keymap and syntax + +(defvar py-shell-map nil + "Keymap used in *Python* shell buffers.") + +;; used by py-completion-at-point, the way of python.el +(defvar python-shell-map + (let ((map (copy-keymap comint-mode-map))) + (define-key map [tab] 'py-shell-complete) + (define-key map "\C-c-" 'py-up-exception) + (define-key map "\C-c=" 'py-down-exception) + map) + "Keymap used in *Python* shell buffers.") + +;;; Intern +(defun py-point (position) + "Returns the value of point at certain commonly referenced POSITIONs. +POSITION can be one of the following symbols: + + bol -- beginning of line + eol -- end of line + bod -- beginning of def or class + eod -- end of def or class + bob -- beginning of buffer + eob -- end of buffer + boi -- back to indentation + bos -- beginning of statement + +This function does not modify point or mark." + (let (erg) + (save-excursion + (setq erg + (progn + (cond + ((eq position 'bol) (beginning-of-line)) + ((eq position 'eol) (end-of-line)) + ((eq position 'bod) (py-beginning-of-def-or-class)) + ((eq position 'eod) (py-end-of-def-or-class)) + ;; Kind of funny, I know, but useful for py-up-exception. + ((eq position 'bob) (goto-char (point-min))) + ((eq position 'eob) (goto-char (point-max))) + ((eq position 'boi) (back-to-indentation)) + ((eq position 'bos) (py-beginning-of-statement)) + (t (error "Unknown buffer position requested: %s" position))) (point)))) + erg)) + + +;;; Font-lock and syntax +(defun python-info-ppss-context (type &optional syntax-ppss) + "Return non-nil if point is on TYPE using SYNTAX-PPSS. +TYPE can be 'comment, 'string or 'paren. It returns the start +character address of the specified TYPE." + (let ((ppss (or syntax-ppss (syntax-ppss)))) + (cond ((eq type 'comment) + (and (nth 4 ppss) + (nth 8 ppss))) + ((eq type 'string) + (nth 8 ppss)) + ((eq type 'paren) + (nth 1 ppss)) + (t nil)))) + +(defvar python-font-lock-keywords nil + "Additional expressions to highlight in Python mode.") + +(setq python-font-lock-keywords + ;; Keywords + `(,(rx symbol-start + (or "and" "del" "from" "not" "while" "as" "elif" "global" "or" "with" + "assert" "else" "if" "pass" "yield" "break" "import" + "print" "exec" "in" "continue" "finally" "is" + "return" "def" "for" "lambda" "try") + symbol-end) + ;; functions + (,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) + (1 font-lock-function-name-face)) + ;; classes + (,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_)))) + (1 font-lock-keyword-face) (2 py-class-name-face)) + (,(rx symbol-start + (or "raise" "except") + symbol-end) . py-exception-name-face) + ;; already pseudo-keyword + ;; (,(rx symbol-start + ;; (or "None" "True" "False" "__debug__" "NotImplemented") + ;; symbol-end) . font-lock-constant-face) + (,(rx symbol-start + (or "cls" "self" "cls" "Ellipsis" "True" "False" "None" "__debug__" "NotImplemented") + symbol-end) . py-pseudo-keyword-face) + ;; Decorators. + (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) + (0+ "." (1+ (or word ?_))))) + (1 py-decorators-face)) + ;; '("\\_<raise[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_.]*\\)" 1 py-exception-name-face) + ;; '("[ \t]*\\(_\\{0,2\\}[a-zA-Z][a-zA-Z_0-9.]+_\\{0,2\\}\\) *\\(+\\|-\\|*\\|*\\*\\|/\\|//\\|&\\|%\\||\\|\\^\\|>>\\|<<\\)? ?=[^=\n]" + ;; Builtin Exceptions + (,(rx symbol-start + (or "ArithmeticError" "AssertionError" "AttributeError" + "BaseException" "BufferError" "BytesWarning" "DeprecationWarning" + "EOFError" "EnvironmentError" "Exception" "FloatingPointError" + "FutureWarning" "GeneratorExit" "IOError" "ImportError" + "ImportWarning" "IndentationError" "IndexError" "KeyError" + "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" + "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) . py-exception-name-face) + ;; (,(rx (or space line-start) symbol-start "range + ;; Builtins + (,(rx (or space line-start) symbol-start + (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" + "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" + "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" + "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" + "execfile" "file" "filter" "float" "format" "frozenset" + "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" + "int" "intern" "isinstance" "issubclass" "iter" "len" "list" + "locals" "long" "map" "max" "min" "next" "object" "oct" "open" + "ord" "pow" "print" "property" "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) . py-builtins-face) + ;; '("[ \t]*\\(_\\{0,2\\}[a-zA-Z][a-zA-Z_0-9.]+_\\{0,2\\}\\) *\\(+\\|-\\|*\\|*\\*\\|/\\|//\\|&\\|%\\||\\|\\^\\|>>\\|<<\\)? ?=[^=\n]" + ;; 1 py-variable-name-face) + (,(python-rx line-start (* (any " \t"))(group (** 0 2 "_") word (0+ (or word ?_))(** 0 2 "_"))(* (any " \t")) assignment-operator) + 1 py-variable-name-face) + ;; asignations + ;; support for a = b = c = 5 + (,(lambda (limit) + (let ((re (python-rx (group (+ (any word ?. ?_))) + (? ?\[ (+ (not (any ?\]))) ?\]) (* space) + assignment-operator))) + (when (re-search-forward re limit t) + (while (and (python-info-ppss-context 'paren) + (re-search-forward re limit t))) + (if (and (not (python-info-ppss-context 'paren)) + (not (equal (char-after (point-marker)) ?=))) + t + (set-match-data nil))))) + (1 py-variable-name-face nil nil)) + ;; support for a, b, c = (1, 2, 3) + (,(lambda (limit) + (let ((re (python-rx (group (+ (any word ?. ?_))) (* space) + (* ?, (* space) (+ (any word ?. ?_)) (* space)) + ?, (* space) (+ (any word ?. ?_)) (* space) + assignment-operator))) + (when (and (re-search-forward re limit t) + (goto-char (nth 3 (match-data)))) + (while (and (python-info-ppss-context 'paren) + (re-search-forward re limit t)) + (goto-char (nth 3 (match-data)))) + (if (not (python-info-ppss-context 'paren)) + t + (set-match-data nil))))) + (1 py-variable-name-face nil nil)) + ;; (,(rx (or space line-start) symbol-start "range" symbol-end) . py-builtins-face) + ;; Numbers + (,(rx symbol-start (or (1+ digit) (1+ hex-digit)) symbol-end) . py-number-face))) + +(defconst py-font-lock-syntactic-keywords + '(("[^\\]\\\\\\(?:\\\\\\\\\\)*\\(\\s\"\\)\\1\\(\\1\\)" + (2 + (7))) + ("\\([RUBrub]?\\)[Rr]?\\(\\s\"\\)\\2\\(\\2\\)" + (1 + (py-quote-syntax 1)) + (2 + (py-quote-syntax 2)) + (3 + (py-quote-syntax 3))))) + +(defun py-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 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 """ \"""" x + (save-excursion + (goto-char (match-beginning 0)) + (cond + ;; Consider property for the last char if in a fenced string. + ((= n 3) + (let* ((font-lock-syntactic-keywords nil) + (syntax (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + (when (eq t (nth 3 syntax)) ; after unclosed fence + (goto-char (nth 8 syntax)) ; fence position + (skip-chars-forward "uUrRbB") ; skip any prefix + ;; 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) ; leading quote (not prefix) + (= (match-beginning 1) (match-end 1))) ; prefix is null + (and (= n 1) ; prefix + (/= (match-beginning 1) (match-end 1)))) ; non-empty + (let ((font-lock-syntactic-keywords nil)) + (unless (eq 'string (syntax-ppss-context (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + ;; (eval-when-compile (string-to-syntax "|")) + (eval-when-compile (string-to-syntax "|"))))) + ;; Otherwise (we're in a non-matching string) the property is + ;; nil, which is OK. + ))) + +(defconst python-dotty-syntax-table + (let ((table (make-syntax-table))) + (set-char-table-parent table py-mode-syntax-table) + (modify-syntax-entry ?. "_" table) + table) + "Syntax table giving `.' symbol syntax. +Otherwise inherits from `py-mode-syntax-table'.") + +;; An auxiliary syntax table which places underscore and dot in the +;; symbol class for simplicity +(defvar py-dotted-expression-syntax-table + (let ((table (make-syntax-table python-mode-syntax-table))) + (modify-syntax-entry ?_ "_" table) + (modify-syntax-entry ?. "_" table) + table) + "Syntax table used to identify Python dotted expressions.") + +;; credits to python.el +(defun py-beg-of-defun-function () + (set (make-local-variable 'beginning-of-defun-function) + 'py-beginning-of-def-or-class)) + +(defun py-end-of-defun-function () + (set (make-local-variable 'end-of-defun-function) 'py-end-of-def-or-class)) + +(make-obsolete-variable 'jpython-mode-hook 'jython-mode-hook nil) +(defvar jython-mode-hook nil + "*Hook called by `jython-mode'. `jython-mode' also calls +`python-mode-hook'.") + +(defvar py-shell-hook nil + "*Hook called by `py-shell'.") + +;; In previous version of python-mode.el, the hook was incorrectly +;; called py-mode-hook, and was not defvar'd. Deprecate its use. +(and (fboundp 'make-obsolete-variable) + (make-obsolete-variable 'py-mode-hook 'python-mode-hook nil)) + +(defvar py-keywords "\\_<\\(ArithmeticError\\|AssertionError\\|AttributeError\\|BaseException\\|BufferError\\|BytesWarning\\|DeprecationWarning\\|EOFError\\|Ellipsis\\|EnvironmentError\\|Exception\\|False\\|FloatingPointError\\|FutureWarning\\|GeneratorExit\\|IOError\\|ImportError\\|ImportWarning\\|IndentationError\\|IndexError\\|KeyError\\|KeyboardInterrupt\\|LookupError\\|MemoryError\\|NameError\\|NoneNotImplementedError\\|NotImplemented\\|OSError\\|OverflowError\\|PendingDeprecationWarning\\|ReferenceError\\|RuntimeError\\|RuntimeWarning\\|StandardError\\|StopIteration\\|SyntaxError\\|SyntaxWarning\\|SystemError\\|SystemExit\\|TabError\\|True\\|TypeError\\|UnboundLocalError\\|UnicodeDecodeError\\|UnicodeEncodeError\\|UnicodeError\\|UnicodeTranslateError\\|UnicodeWarning\\|UserWarning\\|ValueError\\|Warning\\|ZeroDivisionError\\|__debug__\\|__import__\\|__name__\\|abs\\|all\\|and\\|any\\|apply\\|as\\|assert\\|basestring\\|bin\\|bool\\|break\\|buffer\\|bytearray\\|callable\\|chr\\|class\\|classmethod\\|cmp\\|coerce\\|compile\\|complex\\|continue\\|copyright\\|credits\\|def\\|del\\|delattr\\|dict\\|dir\\|divmod\\|elif\\|else\\|enumerate\\|eval\\|except\\|exec\\|execfile\\|exit\\|file\\|filter\\|float\\|for\\|format\\|from\\|getattr\\|global\\|globals\\|hasattr\\|hash\\|help\\|hex\\|id\\|if\\|import\\|in\\|input\\|int\\|intern\\|is\\|isinstance\\|issubclass\\|iter\\|lambda\\|len\\|license\\|list\\|locals\\|long\\|map\\|max\\|memoryview\\|min\\|next\\|not\\|object\\|oct\\|open\\|or\\|ord\\|pass\\|pow\\|print\\|property\\|quit\\|raise\\|range\\|raw_input\\|reduce\\|reload\\|repr\\|return\\|round\\|set\\|setattr\\|slice\\|sorted\\|staticmethod\\|str\\|sum\\|super\\|tuple\\|type\\|unichr\\|unicode\\|vars\\|while\\|with\\|xrange\\|yield\\|zip\\|\\)\\_>" + "Contents like py-fond-lock-keyword") + +(defun py-insert-default-shebang () + "Insert in buffer shebang of installed default Python. " + (interactive "*") + (let* ((erg (if py-edit-only-p + py-shell-name + (executable-find py-shell-name))) + (sheb (concat "#! " erg))) + (insert sheb))) + +(defun py-electric-comment (arg) + "Insert a comment. If starting a comment, indent accordingly. + +If a numeric argument ARG is provided, that many colons are inserted +non-electrically. +With \\[universal-argument] \"#\" electric behavior is inhibited inside a string or comment." + (interactive "*P") + (if (and py-indent-comments py-electric-comment-p) + (if (ignore-errors (eq 4 (car-safe arg))) + (insert "#") + (when (and (eq last-command 'py-electric-comment) (looking-back " ")) + (forward-char -1)) + (if (interactive-p) (self-insert-command (prefix-numeric-value arg)) + (insert "#")) + (let ((orig (copy-marker (point))) + (indent (py-compute-indentation))) + (unless + ;; (or + (eq (current-indentation) indent) + ;; (looking-back "#[ \t]*")) + (goto-char orig) + (beginning-of-line) + (delete-horizontal-space) + (indent-to indent) + (goto-char orig)) + (when py-electric-comment-add-space-p + (unless (looking-at "[ \t]") + (insert " ")))) + (setq last-command this-command)) + (self-insert-command (prefix-numeric-value arg)))) + +(defun py-electric-colon (arg) + "Insert a colon and indent accordingly. + +If a numeric argument ARG is provided, that many colons are inserted +non-electrically. + +Electric behavior is inhibited inside a string or +comment or by universal prefix C-u. +Default is nil, controlled by `py-electric-colon-active-p'" + (interactive "*P") + (cond ((not py-electric-colon-active-p) + (self-insert-command (prefix-numeric-value arg))) + ((eq 4 (prefix-numeric-value arg)) + (self-insert-command 1)) + (t (self-insert-command (prefix-numeric-value arg)) + (unless (py-in-string-or-comment-p) + (let ((orig (copy-marker (point))) + (indent (py-compute-indentation))) + (unless (or (eq (current-indentation) indent) + (and (py-top-level-form-p)(< (current-indentation) indent))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to indent)) + (goto-char orig)))))) + +(defun py-top-level-form-p () + "Return non-nil, if line starts with a top level definition. + +Used by `py-electric-colon', which will not indent than. " + (let (erg) + (save-excursion + (beginning-of-line) + (setq erg (or (looking-at py-class-re) + (looking-at py-def-re)))) + erg)) + + +;; Electric deletion +(defun py-electric-backspace (&optional arg) + "Delete preceding character or level of indentation. + +With ARG do that ARG times. +Returns column reached. " + (interactive "*p") + (let ((arg (or arg 1)) + erg) + (dotimes (i arg) + (if (looking-back "^[ \t]+") + (let* ((remains (% (current-column) py-indent-offset))) + (if (< 0 remains) + (delete-char (- remains)) + (indent-line-to (- (current-indentation) py-indent-offset)))) + (delete-char (- 1)))) + (setq erg (current-column)) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-electric-delete (&optional arg) + "Delete following character or levels of whitespace. + +With ARG do that ARG times. " + (interactive "*p") + (let ((arg (or arg 1))) + (dotimes (i arg) + (if (and (or (bolp)(looking-back "^[ \t]+")) (looking-at "[ \t]+")) + (let* ((remains (% (+ (current-column) (- (match-end 0)(match-beginning 0))) py-indent-offset))) + (if (< 0 remains) + (delete-char remains) + (delete-char py-indent-offset))) + (delete-char 1))))) + +;; (defun py-electric-delete (arg) +;; "Delete preceding or following character or levels of whitespace. +;; +;; The behavior of this function depends on the variable +;; `delete-key-deletes-forward'. If this variable is nil (or does not +;; exist, as in older Emacsen and non-XEmacs versions), then this +;; function behaves identically to \\[c-electric-backspace]. +;; +;; If `delete-key-deletes-forward' is non-nil and is supported in your +;; Emacs, then deletion occurs in the forward direction, by calling the +;; function in `py-delete-function'. +;; +;; \\[universal-argument] (programmatically, argument ARG) specifies the +;; number of characters to delete (default is 1)." +;; (interactive "*p") +;; (if (or (and (fboundp 'delete-forward-p) ;XEmacs 21 +;; (delete-forward-p)) +;; (and (boundp 'delete-key-deletes-forward) ;XEmacs 20 +;; delete-key-deletes-forward)) +;; (funcall py-delete-function arg) +;; (py-electric-backspace arg))) + +;; required for pending-del and delsel modes +(put 'py-electric-colon 'delete-selection t) ;delsel +(put 'py-electric-colon 'pending-delete t) ;pending-del +(put 'py-electric-backspace 'delete-selection 'supersede) ;delsel +(put 'py-electric-backspace 'pending-delete 'supersede) ;pending-del +(put 'py-electric-delete 'delete-selection 'supersede) ;delsel +(put 'py-electric-delete 'pending-delete 'supersede) ;pending-del + + +(defun py-indent-line-outmost (&optional arg) + "Indent the current line to the outmost reasonable indent. + +With optional \\[universal-argument] an indent with length `py-indent-offset' is inserted unconditionally " + (interactive "*P") + (let* ((need (py-compute-indentation (point))) + (cui (current-indentation)) + (cuc (current-column))) + (cond ((eq 4 (prefix-numeric-value arg)) + (insert (make-string py-indent-offset ?\ ))) + (t + (if (and (eq need cui)(not (eq cuc cui))) + (back-to-indentation) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need)))))) + +(defvar py-indent-line-indent nil + "Used internal by `py-indent-line'") + +(defun py-indent-line-intern (need cui) + (if py-tab-indent + (cond ((eq need cui) + (when (eq this-command last-command) + (beginning-of-line) + (delete-horizontal-space) + (if (<= (line-beginning-position) (+ (point) (- col cui))) + (forward-char (- col cui)) + (beginning-of-line)))) + ((< cui need) + (if (eq this-command last-command) + (progn + (beginning-of-line) + (delete-horizontal-space) + (indent-to (+ (* (/ cui py-indent-offset) py-indent-offset) py-indent-offset)) + (forward-char (- col cui))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need) + (forward-char (- col cui)))) + (t (beginning-of-line) + (delete-horizontal-space) + (indent-to need) + (if (<= (line-beginning-position) (+ (point) (- col cui))) + (forward-char (- col cui)) + (beginning-of-line)))) + (insert-tab))) + +(defun py-indent-line (&optional arg) + "Indent the current line according to Python rules. + +When called interactivly with \\[universal-argument], ignore dedenting rules for block closing statements +\(e.g. return, raise, break, continue, pass) + +An optional \\[universal-argument] followed by a numeric argument neither 1 nor 4 will switch off `py-smart-indentation' for this execution. This permits to correct allowed but unwanted indents. +Similar to `toggle-py-smart-indentation' resp. `py-smart-indentation-off' followed by TAB. + +This function is normally used by `indent-line-function' resp. +\\[indent-for-tab-command]. +Returns current indentation " + (interactive "P") + (let ((cui (current-indentation)) + (col (current-column)) + (psi py-smart-indentation) + (need (py-compute-indentation))) + (cond ((eq 4 (prefix-numeric-value arg)) + (beginning-of-line) + (delete-horizontal-space) + (indent-to (+ need py-indent-offset))) + ((not (eq 1 (prefix-numeric-value arg))) + (py-smart-indentation-off) + (py-indent-line-intern) + (setq py-smart-indentation psi)) + (t (py-indent-line-intern need cui)))) + (when (and (interactive-p) py-verbose-p)(message "%s" (current-indentation))) + (current-indentation)) + +(defun py-newline-and-indent () + "Add a newline and indent to outmost reasonable indent. +When indent is set back manually, this is honoured in following lines. " + (interactive "*") + (let ((ci (current-indentation)) + (orig (point)) + erg) + (if (< ci (current-column)) ; if point beyond indentation + (progn + (newline) + (save-excursion + (goto-char orig) (delete-trailing-whitespace)) + (setq erg (indent-to-column (py-compute-indentation)))) + (beginning-of-line) + (insert-char ?\n 1) + (insert (make-string (setq erg (py-compute-indentation)) ?\ )) + ;; (move-to-column erg) + (when (looking-at "\\([ \t]+\\)") (delete-region (match-beginning 1) (match-end 1)))) + (when (and (looking-at "[ \t]+") + (nth 1 (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + (delete-region (match-beginning 0) (match-end 0))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defalias 'py-newline-and-close-block 'py-newline-and-dedent) +(defun py-newline-and-dedent () + "Add a newline and indent to one level below current. +Returns column. " + (interactive "*") + (let ((cui (current-indentation)) + erg) + (newline) + (when (< 0 cui) + (setq erg (- (py-compute-indentation) py-indent-offset)) + (indent-to-column erg)) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun toggle-force-local-shell (&optional arg) + "If locally indicated Python shell should be taken and +enforced upon sessions execute commands. + +Toggles boolean `py-force-local-shell-p' along with `py-force-py-shell-name-p' +Returns value of `toggle-force-local-shell' switched to. + +When on, kind of an option 'follow', local shell sets `py-shell-name', enforces its use afterwards. + +See also commands +`py-force-local-shell-on' +`py-force-local-shell-off' + " + (interactive (list arg)) + (let ((arg (or arg (if py-force-local-shell-p -1 1)))) + (if (< 0 arg) + (progn + (setq py-shell-name (or py-local-command (py-choose-shell))) + (setq py-force-local-shell-p t)) + (setq py-shell-name (default-value 'py-shell-name)) + (setq py-force-local-shell-p nil)) + (when (interactive-p) + (if py-force-local-shell-p + (when py-verbose-p (message "Enforce %s" py-shell-name)) + (when py-verbose-p (message "py-shell-name default restored to: %s" py-shell-name)))) + py-shell-name)) + +(defun py-force-local-shell-on () + "Make sure, `py-py-force-local-shell-p' is on. + +Returns value of `py-force-local-shell-p'. + +Kind of an option 'follow', local shell sets `py-shell-name', enforces its use afterwards " + (interactive) + (let* ((erg (toggle-force-local-shell 1))) + (when (or py-verbose-p (interactive-p)) + (message "Enforce %s" py-shell-name)))) + +(defun py-force-local-shell-off () + "Restore `py-shell-name' default value and `behaviour'. " + (interactive) + (let* ((erg (toggle-force-local-shell 1))) + (when (or py-verbose-p (interactive-p)) + (message "py-shell-name default restored to: %s" py-shell-name) + (message "Enforce %s" py-shell-name)))) + +;; toggle-force-py-shell-name-p forms +(defun toggle-force-py-shell-name-p (&optional arg) + "If customized default `py-shell-name' should be enforced upon execution. + +If `py-force-py-shell-name-p' should be on or off. +Returns value of `py-force-py-shell-name-p' switched to. + +See also commands +force-py-shell-name-p-on +force-py-shell-name-p-off + +Caveat: Completion might not work that way. +" + (interactive "p") + (let ((arg (or arg (if py-force-py-shell-name-p -1 1)))) + (if (< 0 arg) + (setq py-force-py-shell-name-p t) + (setq py-force-py-shell-name-p nil)) + (when (or py-verbose-p (interactive-p)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p)) + +(defun force-py-shell-name-p-on (&optional arg) + "Switches `py-force-py-shell-name-p' on. + +Customized default `py-shell-name' will be enforced upon execution. +Returns value of `py-force-py-shell-name-p'. + +Caveat: Completion might not work that way. +" + (interactive "p") + (let ((arg (or arg 1))) + (toggle-force-py-shell-name-p arg)) + (when (or py-verbose-p (interactive-p)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p) + +(defun force-py-shell-name-p-off () + "Make sure, `py-force-py-shell-name-p' is off. + +Function to use by executes will be guessed from environment. +Returns value of `py-force-py-shell-name-p'. " + (interactive) + (toggle-force-py-shell-name-p -1) + (when (or py-verbose-p (interactive-p)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p) + +(defun py-toggle-indent-tabs-mode () + "Toggle `indent-tabs-mode'. + +Returns value of `indent-tabs-mode' switched to. " + (interactive) + (when + (setq indent-tabs-mode (not indent-tabs-mode)) + (setq tab-width py-indent-offset)) + (when (and py-verbose-p (interactive-p)) (message "indent-tabs-mode %s py-indent-offset %s" indent-tabs-mode py-indent-offset)) + indent-tabs-mode) + +(defun py-indent-tabs-mode (arg &optional iact) + "With positive ARG switch `indent-tabs-mode' on. + +With negative ARG switch `indent-tabs-mode' off. +Returns value of `indent-tabs-mode' switched to. " + (interactive "p") + (if (< 0 arg) + (progn + (setq indent-tabs-mode t) + (setq tab-width py-indent-offset)) + (setq indent-tabs-mode nil)) + (when (and py-verbose-p (or iact (interactive-p))) (message "indent-tabs-mode %s py-indent-offset %s" indent-tabs-mode py-indent-offset)) + indent-tabs-mode) + +(defun py-indent-tabs-mode-on (arg) + "Switch `indent-tabs-mode' on. " + (interactive "p") + (indent-tabs-mode (abs arg)(interactive-p))) + +(defun py-indent-tabs-mode-off (arg) + "Switch `indent-tabs-mode' on. " + (interactive "p") + (indent-tabs-mode (- (abs arg))(interactive-p))) + +;;; Guess indent offset +(defun py-guessed-sanity-check (guessed) + (and (>= guessed 2)(<= guessed 8)(eq 0 (% guessed 2)))) + +(defun py-guess-indent-offset (&optional global orig origline) + "Guess a value for, and change, `py-indent-offset'. + +By default, make a buffer-local copy of `py-indent-offset' with the +new value. +With optional argument GLOBAL change the global value of `py-indent-offset'. + +Indent might be guessed savely only from beginning of a block. +Returns `py-indent-offset'" + (interactive "P") + (save-excursion + (let* ((orig (or orig (point))) + (origline (or origline (py-count-lines))) + (firstindent + (if (eq origline (py-count-lines)) + (progn (py-beginning-of-statement) + (if (eq origline (py-count-lines)) + (progn (py-beginning-of-statement)(current-column)) (current-column))))) + (erg (when firstindent + (py-beginning-of-block) + (if + (< (current-column) firstindent) + (current-column) + (progn (goto-char orig) + ;; need a block-start + (when + (setq firstindent (progn (py-beginning-of-block)(current-indentation))) + (when (eq origline (py-count-lines)) + (setq firstindent (progn (py-beginning-of-block)(current-indentation)))) + (when (ignore-errors (< firstindent (py-down-statement))) + (current-indentation))))))) + (guessed (when erg (abs (- firstindent erg))))) + (if (and guessed (py-guessed-sanity-check guessed)) + (setq py-indent-offset guessed) + (setq py-indent-offset (default-value 'py-indent-offset))) + (funcall (if global 'kill-local-variable 'make-local-variable) + 'py-indent-offset) + (when (and py-verbose-p (interactive-p)) + (message "%s value of py-indent-offset: %d" + (if global "Global" "Local") + py-indent-offset)) + py-indent-offset))) + +;;; +(defun py-comment-indent-function () + "Python version of `comment-indent-function'." + ;; This is required when filladapt is turned off. Without it, when + ;; filladapt is not used, comments which start in column zero + ;; cascade one character to the right + (save-excursion + (beginning-of-line) + (let ((eol (line-end-position))) + (and comment-start-skip + (re-search-forward comment-start-skip eol t) + (setq eol (match-beginning 0))) + (goto-char eol) + (skip-chars-backward " \t") + (max comment-column (+ (current-column) (if (bolp) 0 1)))))) + +(defun py-narrow-to-defun () + "Make text outside current def or class invisible. + +The defun visible is the one that contains point or follows point. " + (interactive "P") + (save-excursion + (widen) + (py-end-of-def-or-class) + (let ((end (point))) + (py-beginning-of-def-or-class) + (narrow-to-region (point) end)))) + +;; make general form below work also in these cases +(defalias 'py-beginning-of-paragraph 'backward-paragraph) +(defalias 'py-end-of-paragraph 'forward-paragraph) + +;;; Shifting +(defalias 'py-shift-region-left 'py-shift-left) +(defun py-shift-left (&optional count start end) + "Dedent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is dedented. +Returns indentation reached. " + (interactive "p") + (let ((erg (py-shift-intern (- count) start end))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defalias 'py-shift-region-right 'py-shift-right) +(defun py-shift-right (&optional count beg end) + "Indent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is indented. +Returns indentation reached. " + (interactive "p") + (let ((erg (py-shift-intern count beg end))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-intern (count &optional start end) + (save-excursion + (let* ((inhibit-point-motion-hooks t) + deactivate-mark + (beg (cond (start) + ((region-active-p) + (save-excursion + (goto-char + (region-beginning)))) + (t (line-beginning-position)))) + (end (cond (end) + ((region-active-p) + (save-excursion + (goto-char + (region-end)))) + (t (line-end-position)))) + (orig end)) + (setq beg (copy-marker beg)) + (setq end (copy-marker end)) + ;; lp:962227 + ;; (dotimes (i (abs count)) + (if (< 0 count) + (indent-rigidly beg end py-indent-offset) + (indent-rigidly beg end (- py-indent-offset))) + ;; ) + (push-mark beg t) + (goto-char end) + (skip-chars-backward " \t\r\n\f")) + (py-indentation-of-statement))) + +(defun py-shift-forms-base (form arg &optional beg end) + (let* ((begform (intern-soft (concat "py-beginning-of-" form))) + (endform (intern-soft (concat "py-end-of-" form))) + (orig (copy-marker (point))) + (beg (cond (beg) + ((region-active-p) + (save-excursion + (goto-char (region-beginning)) + (line-beginning-position))) + (t (save-excursion + (funcall begform) + (line-beginning-position))))) + (end (cond (end) + ((region-active-p) + (region-end)) + (t (funcall endform)))) + (erg (py-shift-intern arg beg end))) + (goto-char orig) + erg)) + +(defun py-shift-paragraph-right (&optional arg) + "Indent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "paragraph" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-paragraph-left (&optional arg) + "Dedent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "paragraph" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-block-right (&optional arg) + "Indent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "block" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-block-left (&optional arg) + "Dedent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "block" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-clause-right (&optional arg) + "Indent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "clause" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-clause-left (&optional arg) + "Dedent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "clause" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-def-right (&optional arg) + "Indent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "def" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-def-left (&optional arg) + "Dedent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "def" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-class-right (&optional arg) + "Indent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "class" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-class-left (&optional arg) + "Dedent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "class" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-line-right (&optional arg) + "Indent line by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "line" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-line-left (&optional arg) + "Dedent line by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "line" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-statement-right (&optional arg) + "Indent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "statement" (or arg py-indent-offset)))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-shift-statement-left (&optional arg) + "Dedent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Returns outmost indentation reached. " + (interactive "*P") + (let ((erg (py-shift-forms-base "statement" (- (or arg py-indent-offset))))) + (when (and (interactive-p) py-verbose-p) (message "%s" erg)) + erg)) + +(defun py-indent-and-forward () + "Indent current line according to mode, move one line forward. " + (interactive "*") + (beginning-of-line) + (fixup-whitespace) + (indent-to (py-compute-indentation)) + (if (eobp) + (newline-and-indent) + (forward-line 1)) + (back-to-indentation)) + +(defun py-indent-region (start end &optional indent-offset) + "Reindent a region of Python code. + +With optional INDENT-OFFSET specify a different value than `py-indent-offset' at place. + +Guesses the outmost reasonable indent +Returns and keeps relative position " + (interactive "*r\nP") + (let ((orig (copy-marker (point))) + (beg start) + (end (copy-marker end)) + (py-indent-offset (prefix-numeric-value + (or indent-offset py-indent-offset)))) + (goto-char beg) + (while (< (line-end-position) end) + (py-indent-and-forward)) + (unless (empty-line-p) (py-indent-line)) + (goto-char orig))) + +;;; Positions +(defun py-beginning-of-paragraph-position () + "Returns beginning of paragraph position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-paragraph))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-paragraph-position () + "Returns end of paragraph position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-paragraph))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-block-position () + "Returns beginning of block position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-block))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-block-position () + "Returns end of block position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-block))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-clause-position () + "Returns beginning of clause position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-clause))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-clause-position () + "Returns end of clause position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-clause))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-block-or-clause-position () + "Returns beginning of block-or-clause position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-block-or-clause))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-block-or-clause-position () + "Returns end of block-or-clause position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-block-or-clause))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-def-position () + "Returns beginning of def position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-def))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-def-position () + "Returns end of def position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-def))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-class-position () + "Returns beginning of class position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-class))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-class-position () + "Returns end of class position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-class))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-def-or-class-position () + "Returns beginning of def-or-class position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-def-or-class))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-def-or-class-position () + "Returns end of def-or-class position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-def-or-class))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-line-position () + "Returns beginning of line position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-line))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-line-position () + "Returns end of line position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-line))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-statement-position () + "Returns beginning of statement position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-statement))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-statement-position () + "Returns end of statement position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-statement))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-expression-position () + "Returns beginning of expression position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-expression))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-expression-position () + "Returns end of expression position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-expression))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-partial-expression-position () + "Returns beginning of partial-expression position. " + (interactive) + (save-excursion + (let ((erg (py-beginning-of-partial-expression))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-end-of-partial-expression-position () + "Returns end of partial-expression position. " + (interactive) + (save-excursion + (let ((erg (py-end-of-partial-expression))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +;;; Bounds +(defun py-bounds-of-statement (&optional position) + "Returns bounds of statement at point. + +With optional POSITION, a number, report bounds of statement at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-statement-position)) + (end (py-end-of-statement-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-block (&optional position) + "Returns bounds of block at point. + +With optional POSITION, a number, report bounds of block at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-block-position)) + (end (py-end-of-block-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-clause (&optional position) + "Returns bounds of clause at point. + +With optional POSITION, a number, report bounds of clause at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-clause-position)) + (end (py-end-of-clause-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-block-or-clause (&optional position) + "Returns bounds of block-or-clause at point. + +With optional POSITION, a number, report bounds of block-or-clause at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-block-or-clause-position)) + (end (py-end-of-block-or-clause-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-def (&optional position) + "Returns bounds of def at point. + +With optional POSITION, a number, report bounds of def at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-def-position)) + (end (py-end-of-def-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-class (&optional position) + "Returns bounds of class at point. + +With optional POSITION, a number, report bounds of class at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-class-position)) + (end (py-end-of-class-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-region () + "Returns bounds of region at point. + +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (let ((beg (region-beginning)) + (end (region-end))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-beginning-of-buffer-position () + (point-min)) + +(defun py-end-of-buffer-position () + (point-max)) + +(defun py-bounds-of-buffer (&optional position) + "Returns bounds of buffer at point. + +With optional POSITION, a number, report bounds of buffer at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-buffer-position)) + (end (py-end-of-buffer-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-expression (&optional position) + "Returns bounds of expression at point. + +With optional POSITION, a number, report bounds of expression at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-expression-position)) + (end (py-end-of-expression-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +(defun py-bounds-of-partial-expression (&optional position) + "Returns bounds of partial-expression at point. + +With optional POSITION, a number, report bounds of partial-expression at POSITION. +Returns a list, whose car is beg, cdr - end." + (interactive) + (save-excursion + (save-restriction + (widen) + (when position (goto-char position)) + (let ((beg (py-beginning-of-partial-expression-position)) + (end (py-end-of-partial-expression-position))) + (if (and beg end) + (when (and py-verbose-p (interactive-p)) (message "%s" (list beg end))) + (list beg end)))))) + +;;; Declarations +(defun py-bounds-of-declarations () + "Bounds of consecutive multitude of assigments resp. statements around point. + +Indented same level, which don't open blocks. +Typically declarations resp. initialisations of variables following +a class or function definition. +See also py-bounds-of-statements " + (interactive) + (let* ((orig-indent (progn + (back-to-indentation) + (unless (py-beginning-of-statement-p) + (py-beginning-of-statement)) + (unless (py-beginning-of-block-p) + (current-indentation)))) + (orig (point)) + last beg end) + (when orig-indent + (setq beg (line-beginning-position)) + ;; look upward first + (while (and + (progn + (unless (py-beginning-of-statement-p) + (py-beginning-of-statement)) + (line-beginning-position)) + (py-beginning-of-statement) + (not (py-beginning-of-block-p)) + (eq (current-indentation) orig-indent)) + (setq beg (line-beginning-position))) + (goto-char orig) + (while (and (setq last (line-end-position)) + (setq end (py-down-statement)) + (not (py-beginning-of-block-p)) + (eq (py-indentation-of-statement) orig-indent))) + (setq end last) + (goto-char beg) + (if (and beg end) + (progn + (when (and py-verbose-p (interactive-p)) (message "%s %s" beg end)) + (cons beg end)) + (when (and py-verbose-p (interactive-p)) (message "%s" nil)) + nil)))) + +(defalias 'py-backward-declarations 'py-beginning-of-declarations) +(defun py-beginning-of-declarations () + "Got to the beginning of assigments resp. statements in current level which don't open blocks. +" + (interactive) + (let* ((bounds (py-bounds-of-declarations)) + (erg (car bounds))) + (when erg (goto-char erg)) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-forward-of-declarations 'py-end-of-declarations) +(defun py-end-of-declarations () + "Got to the end of assigments resp. statements in current level which don't open blocks. " + (interactive) + (let* ((bounds (py-bounds-of-declarations)) + (erg (cdr bounds))) + (when erg (goto-char erg)) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-copy-declarations 'py-declarations) +(defun py-declarations () + "Copy and mark assigments resp. statements in current level which don't open blocks or start with a keyword. + +See also `py-statements', which is more general, taking also simple statements starting with a keyword. " + (interactive) + (let* ((bounds (py-bounds-of-declarations)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (exchange-point-and-mark)))) + +(defun py-kill-declarations () + "Delete variables declared in current level. + +Store deleted variables in kill-ring " + (interactive "*") + (let* ((bounds (py-bounds-of-declarations)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (delete-region beg end)))) + +;;; Statements +(defun py-bounds-of-statements () + "Bounds of consecutive multitude of statements around point. + +Indented same level, which don't open blocks. " + (interactive) + (let* ((orig-indent (progn + (back-to-indentation) + (unless (py-beginning-of-statement-p) + (py-beginning-of-statement)) + (unless (py-beginning-of-block-p) + (current-indentation)))) + (orig (point)) + last beg end) + (when orig-indent + (setq beg (point)) + (while (and (setq last beg) + (setq beg + (when (py-beginning-of-statement) + (line-beginning-position))) + (not (py-in-string-p)) + (not (py-beginning-of-block-p)) + (eq (current-indentation) orig-indent))) + (setq beg last) + (goto-char orig) + (setq end (line-end-position)) + (while (and (setq last (line-end-position)) + (setq end (py-down-statement)) + (not (py-beginning-of-block-p)) + ;; (not (looking-at py-keywords)) + ;; (not (looking-at "pdb\.")) + (not (py-in-string-p)) + (eq (py-indentation-of-statement) orig-indent))) + (setq end last) + (goto-char orig) + (if (and beg end) + (progn + (when (and py-verbose-p (interactive-p)) (message "%s %s" beg end)) + (cons beg end)) + (when (and py-verbose-p (interactive-p)) (message "%s" nil)) + nil)))) + +(defalias 'py-backward-statements 'py-beginning-of-statements) +(defun py-beginning-of-statements () + "Got to the beginning of statements in current level which don't open blocks. " + (interactive) + (let* ((bounds (py-bounds-of-statements)) + (erg (car bounds))) + (when erg (goto-char erg)) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-forward-of-statements 'py-end-of-statements) +(defun py-end-of-statements () + "Got to the end of statements in current level which don't open blocks. " + (interactive) + (let* ((bounds (py-bounds-of-statements)) + (erg (cdr bounds))) + (when erg (goto-char erg)) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-copy-statements 'py-statements) +(defun py-statements () + "Copy and mark simple statements in current level which don't open blocks. + +More general than py-declarations, which would stop at keywords like a print-statement. " + (interactive) + (let* ((bounds (py-bounds-of-statements)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (exchange-point-and-mark)))) + +(defun py-kill-statements () + "Delete statements declared in current level. + +Store deleted statements in kill-ring " + (interactive "*") + (let* ((bounds (py-bounds-of-statements)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (delete-region beg end)))) + +;;; Comments, Filling +(defun py-comment-region (beg end &optional arg) + "Like `comment-region' but uses double hash (`#') comment starter." + (interactive "r\nP") + (let ((comment-start py-block-comment-prefix)) + (comment-region beg end arg))) + +(defun py-join-words-wrapping (words separator line-prefix line-length) + (let ((lines ()) + (current-line line-prefix)) + (while words + (let* ((word (car words)) + (maybe-line (concat current-line word separator))) + (if (> (length maybe-line) line-length) + (setq lines (cons (substring current-line 0 -1) lines) + current-line (concat line-prefix word separator " ")) + (setq current-line (concat maybe-line " ")))) + (setq words (cdr words))) + (setq lines (cons (substring + current-line 0 (- 0 (length separator) 1)) lines)) + (mapconcat 'identity (nreverse lines) "\n"))) + +(defun py-fill-comment (&optional justify) + "Fill the comment paragraph at point" + (let (;; Non-nil if the current line contains a comment. + has-comment + + ;; If has-comment, the appropriate fill-prefix for the comment. + comment-fill-prefix) + + ;; Figure out what kind of comment we are looking at. + (save-excursion + (beginning-of-line) + (cond + ;; A line with nothing but a comment on it? + ((looking-at "[ \t]*#[# \t]*") + (setq has-comment t + comment-fill-prefix (buffer-substring (match-beginning 0) + (match-end 0)))) + + ;; A line with some code, followed by a comment? Remember that the hash + ;; which starts the comment shouldn't be part of a string or character. + ((progn + (while (not (looking-at "#\\|$")) + (skip-chars-forward "^#\n\"'\\") + (cond + ((eq (char-after (point)) ?\\) (forward-char 2)) + ((memq (char-after (point)) '(?\" ?')) (forward-sexp 1)))) + (looking-at "#+[\t ]*")) + (setq has-comment t) + (setq comment-fill-prefix + (concat (make-string (current-column) ? ) + (buffer-substring (match-beginning 0) (match-end 0))))))) + + (if (not has-comment) + (fill-paragraph justify) + + ;; Narrow to include only the comment, and then fill the region. + (save-restriction + (narrow-to-region + + ;; Find the first line we should include in the region to fill. + (save-excursion + (while (and (zerop (forward-line -1)) + (looking-at "^[ \t]*#"))) + + ;; We may have gone to far. Go forward again. + (or (looking-at "^[ \t]*#") + (forward-line 1)) + (point)) + + ;; Find the beginning of the first line past the region to fill. + (save-excursion + (while (progn (forward-line 1) + (looking-at "^[ \t]*#"))) + (point))) + + ;; Lines with only hashes on them can be paragraph boundaries. + (let ((paragraph-start (concat paragraph-start "\\|[ \t#]*$")) + (paragraph-separate (concat paragraph-separate "\\|[ \t#]*$")) + (fill-prefix comment-fill-prefix)) + ;;(message "paragraph-start %S paragraph-separate %S" + ;;paragraph-start paragraph-separate) + (fill-paragraph justify)))) + t)) + +(defun py-fix-this-indent (indent) + (unless (and (eq (current-indentation) (current-column)) + (eq (current-column) indent)) + (beginning-of-line) + (indent-to-column indent) + (delete-region + (point) + (progn (skip-chars-forward " \t") (point))))) + +(defun py-fill-string (start &optional justify) + "Fill the paragraph around (point) in the string starting at start" + ;; basic strategy: narrow to the string and call the default + ;; implementation + (let (;; the start of the string's contents + string-start + ;; the end of the string's contents + string-end + ;; length of the string's delimiter + delim-length + ;; The string delimiter + delim) + + (save-excursion + (goto-char start) + (if (looking-at "\\([urbURB]*\\(?:'''\\|\"\"\"\\|'\\|\"\\)\\)\\\\?\n?") + (setq string-start (match-end 0) + delim-length (- (match-end 1) (match-beginning 1)) + delim (buffer-substring-no-properties (match-beginning 1) + (match-end 1))) + (error "The parameter start is not the beginning of a python string")) + + ;; if the string is the first token on a line and doesn't start with + ;; a newline, fill as if the string starts at the beginning of the + ;; line. this helps with one line docstrings + (save-excursion + (beginning-of-line) + (and (/= (char-before string-start) ?\n) + (looking-at (concat "[ \t]*" delim)) + (setq string-start (point)))) + + ;; move until after end of string, then the end of the string's contents + ;; is delim-length characters before that + (forward-sexp) + (setq string-end (- (point) delim-length))) + + ;; Narrow to the string's contents and fill the current paragraph + (save-restriction + (narrow-to-region string-start string-end) + (let ((ends-with-newline (= (char-before (point-max)) ?\n))) + (fill-paragraph justify) + (if (and (not ends-with-newline) + (= (char-before (point-max)) ?\n)) + ;; the default fill-paragraph implementation has inserted a + ;; newline at the end. Remove it again. + (save-excursion + (goto-char (point-max)) + (delete-char -1))))) + + ;; return t to indicate that we've done our work + t)) + +(defun py-fill-paragraph (&optional justify) + "Like \\[fill-paragraph], but handle Python comments and 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 `#'s. +If point is inside a string, narrow to that string and fill. +" + (interactive "P") + (save-excursion + (save-restriction + (widen) + (let ((pps + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + (cond + ;; inside a comment + ((nth 4 pps) + (py-fill-comment justify)) + ;; only whitespace before the comment start + ((save-excursion (beginning-of-line) (looking-at "[ \t]*#")) + (py-fill-comment justify)) + ;; inside a string + ((nth 3 pps) + (py-fill-string (nth 8 pps))) + ;; opening quote of a string + ((progn (save-excursion (forward-char 1)(nth 3 pps))) + (save-excursion + (forward-char 1) + (py-fill-string (nth 8 pps))))))))) + +(defun py-insert-super () + "Insert a function \"super()\" from current environment. + +As example given in Python v3.1 documentation » The Python Standard Library » + +class C(B): + def method(self, arg): + super().method(arg) # This does the same thing as: + # super(C, self).method(arg) + +Returns the string inserted. " + (interactive "*") + (let* ((orig (point)) + (funcname (progn + (py-beginning-of-def) + (when (looking-at (concat py-def-re " *\\([^(]+\\) *(\\(?:[^),]*\\),? *\\([^)]*\\))")) + (match-string-no-properties 2)))) + (args (match-string-no-properties 3)) + (ver (py-which-python)) + classname erg) + (if (< ver 3) + (progn + (py-beginning-of-class) + (when (looking-at (concat py-class-re " *\\([^( ]+\\)")) + (setq classname (match-string-no-properties 2))) + (goto-char orig) + (setq erg (concat "super(" classname ", self)." funcname "(" args ")")) + ;; super(C, self).method(arg)" + (insert erg)) + (goto-char orig) + (setq erg (concat "super()." funcname "(" args ")")) + (insert erg)) + erg)) + +(defun py-nesting-level (&optional pps) + "Accepts the output of `parse-partial-sexp'. " + (interactive) + (let* ((pps (or (ignore-errors (nth 0 pps)) + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + (erg (nth 0 pps))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-count-indentation 'py-compute-indentation) +(defun py-compute-indentation (&optional orig origline closing line inside repeat) + "Compute Python indentation. + +When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return', +`raise', `break', `continue', and `pass' force one level of dedenting." + (interactive "P") + (save-excursion + (save-restriction + (widen) + (let* ((orig (or orig (point))) + (origline (or origline (py-count-lines))) + ;; closing indicates: when started, looked + ;; at a single closing parenthesis + (closing closing) + ;; line: moved already a line backward + (line line) + (pps (syntax-ppss)) + ;; in a recursive call already + (repeat repeat) + ;; inside: started inside a list + (inside inside) + erg indent this-line) + (unless repeat (setq inside (nth 1 pps)) + (setq repeat t)) + (setq indent + (cond + ((and (bobp) + (eq origline (py-count-lines))) + (current-indentation)) + ((and (bobp)(py-statement-opens-block-p)) + (+ (if py-smart-indentation (py-guess-indent-offset nil orig origline) py-indent-offset) (current-indentation))) + ((and (bobp)(not (py-statement-opens-block-p))) + (current-indentation)) + ;; (py-in-triplequoted-string-p) + ((and (nth 3 pps)(nth 8 pps)) + (if (eq origline (py-count-lines)) + (progn + (forward-line -1) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (if (ignore-errors (< (nth 2 (syntax-ppss)) (line-beginning-position))) + (current-indentation) + (ignore-errors (goto-char (nth 2 pps))) + (py-line-backward-maybe) + (back-to-indentation) + (py-compute-indentation orig origline closing line inside repeat))) + (current-indentation))) + ((and (looking-at "\"\"\"\\|'''")(not (bobp))) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat)) + ;; comments + ((nth 8 pps) + (if (eq origline (py-count-lines)) + (progn + (goto-char (nth 8 pps)) + (py-line-backward-maybe) + (skip-chars-backward " \t") + (py-compute-indentation orig origline closing line inside repeat)) + (goto-char (nth 8 pps)) + (if (and line (or py-indent-honors-inline-comment (looking-back "^[ \t]*"))) + (current-column) + (forward-char -1) + (py-compute-indentation orig origline closing line inside repeat)))) + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*")(not py-indent-comments)(eq origline (py-count-lines))) + 0) + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*")(not (eq (line-beginning-position) (point-min)))) + (forward-line -1) + (end-of-line) + (setq line t) + (py-compute-indentation orig origline closing line inside repeat)) + ;; lists + ((nth 1 pps) + (cond ((and inside (not line)) + (when (and (eq (point) orig) (looking-at "[ \t]*\\()\\)[ \t]*$")) + (setq closing (match-beginning 0))) + (save-excursion + (goto-char (nth 1 pps)) + (setq this-line (py-count-lines)) + (cond + ((< 0 (- origline this-line)) + (if (< 1 (- origline this-line)) + (if closing + (if py-closing-list-dedents-bos + (current-indentation) + (+ (current-indentation) py-indent-offset)) + (py-fetch-previous-indent orig)) + (cond ((looking-at "\\s([ \t]*$") + (if + (progn + (save-excursion + (back-to-indentation) + (looking-at py-block-or-clause-re))) + (progn + (back-to-indentation) + (+ (current-column) (* 2 py-indent-offset))) + (back-to-indentation) + (+ (current-column) py-indent-offset))) + ((looking-at "\\s([ \t]*\\([^ \t]+.*\\)$") + (goto-char (match-beginning 1)) + (current-column)) + (t (+ (current-column) (* (nth 0 pps))))))) + (t (back-to-indentation) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat))))) + ((and (not inside) line) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat)) + ((not inside) + (progn (goto-char (+ py-lhs-inbound-indent (nth 1 pps))) + (when (looking-at "[ \t]+") + (goto-char (match-end 0))) + (current-column))) + (t + (goto-char (nth 1 pps)) + (py-compute-indentation orig origline closing line inside repeat)))) + ((py-preceding-line-backslashed-p) + (progn + (py-beginning-of-statement) + (setq this-line (py-count-lines)) + (if (< 1 (- origline this-line)) + (py-fetch-previous-indent orig) + (if (looking-at "from +\\([^ \t\n]+\\) +import") + 5 + (+ (current-indentation) py-continuation-offset))))) + ((looking-at py-no-outdent-re) + (if (eq (py-count-lines) origline) + (progn + (back-to-indentation) + (py-line-backward-maybe) + (py-compute-indentation orig origline closing line inside repeat)) + (current-indentation))) + ((and (looking-at py-block-closing-keywords-re)(eq (py-count-lines) origline)) + (py-beginning-of-block-or-clause) + (+ + (if py-smart-indentation (py-guess-indent-offset nil orig origline) py-indent-offset) + ;; py-indent-offset + (current-indentation))) + ((looking-at py-block-closing-keywords-re) + (py-beginning-of-block-or-clause (current-indentation)) + (current-indentation)) + ((and (looking-at py-elif-re) (eq (py-count-lines) origline)) + (py-line-backward-maybe) + (car (py-clause-lookup-keyword py-elif-re -1 nil orig origline))) + ((and (looking-at py-clause-re)(eq origline (py-count-lines))) + (cond ((looking-at py-finally-re) + (car (py-clause-lookup-keyword py-finally-re -1 nil orig origline))) + ((looking-at py-except-re) + (car (py-clause-lookup-keyword py-except-re -1 nil orig origline))) + ((looking-at py-else-re) + ;; (car (py-clause-lookup-keyword py-else-re -1 (current-indentation)))) + (car (py-clause-lookup-keyword py-else-re -1 nil orig origline))) + ((looking-at py-elif-re) + (car (py-clause-lookup-keyword py-elif-re -1 nil orig origline))) + ;; maybe at if, try, with + (t (car (py-clause-lookup-keyword py-block-or-clause-re -1 nil orig origline))))) + ((looking-at py-block-or-clause-re) + (cond ((eq origline (py-count-lines)) + (py-line-backward-maybe) + (py-compute-indentation orig origline closing line inside t)) + (t (+ (if py-smart-indentation (py-guess-indent-offset nil orig origline) py-indent-offset)(current-indentation))))) + ((looking-at py-block-closing-keywords-re) + (py-beginning-of-block) + (current-indentation)) + ((and (< (current-indentation) (current-column))) + (back-to-indentation) + (unless line + (setq inside (nth 1 (syntax-ppss)))) + (py-compute-indentation orig origline closing line inside repeat)) + ((not (py-beginning-of-statement-p)) + (if (bobp) + (current-column) + (if (eq (point) orig) + (progn + (py-line-backward-maybe) + (py-compute-indentation orig origline closing line inside repeat)) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat)))) + ((py-statement-opens-block-p) + (if (< (py-count-lines) origline) + (+ (if py-smart-indentation (py-guess-indent-offset nil orig origline) py-indent-offset) (current-indentation)) + (py-compute-indentation orig origline closing line inside t))) + ((and (< (py-count-lines) origline)(looking-at py-assignment-re)) + (current-indentation)) + ((looking-at py-assignment-re) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat)) + ((and (eq origline (py-count-lines)) + (save-excursion (and (setq erg (py-go-to-keyword py-block-or-clause-re)) + (ignore-errors (< orig (py-end-of-block-or-clause)))))) + (+ (car erg) (if py-smart-indentation (py-guess-indent-offset nil orig origline) py-indent-offset))) + ((and (eq origline (py-count-lines)) + (py-beginning-of-statement-p)) + (py-beginning-of-statement) + (py-compute-indentation orig origline closing line inside repeat)) + (t (current-indentation)))) + (when (and py-verbose-p (interactive-p)) (message "%s" indent)) + indent)))) + +(defun py-line-backward-maybe () + (skip-chars-backward " \t\f" (line-beginning-position)) + (when (< 0 (abs (skip-chars-backward " \t\r\n\f"))) + (setq line t))) + +(defun py-fetch-previous-indent (orig) + "Report the preceding indent. " + (save-excursion + (goto-char orig) + (forward-line -1) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (current-indentation))) + +(defun py-continuation-offset (&optional arg) + "With numeric ARG different from 1 py-continuation-offset is set to that value; returns py-continuation-offset. " + (interactive "p") + (let ((erg (if (eq 1 arg) + py-continuation-offset + (when (numberp arg) + (prog1 + arg + (setq py-continuation-offset arg)))))) + (when (and py-verbose-p (interactive-p)) (message "%s" py-continuation-offset)) + py-continuation-offset)) + +(defalias 'pios 'py-indentation-of-statement) +(defalias 'ios 'py-indentation-of-statement) +(defun py-indentation-of-statement () + "Returns the indenation of the statement at point. " + (interactive) + (let ((erg (save-excursion + (back-to-indentation) + (or (py-beginning-of-statement-p) + (py-beginning-of-statement)) + (current-indentation)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-in-list-p 'py-list-beginning-position) +(defun py-list-beginning-position (&optional start) + "Return lists beginning position, nil if not inside. + +Optional ARG indicates a start-position for `parse-partial-sexp'." + (interactive) + (let* ((ppstart (or start (point-min))) + (erg (nth 1 (syntax-ppss)))) + (when (interactive-p) (message "%s" erg)) + erg)) + +(defun py-end-of-list-position (&optional arg) + "Return end position, nil if not inside. + +Optional ARG indicates a start-position for `parse-partial-sexp'." + (interactive) + (let* ((ppstart (or arg (point-min))) + (erg (syntax-ppss)) + (beg (nth 1 erg)) + end) + (when beg + (save-excursion + (goto-char beg) + (forward-list 1) + (setq end (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" end)) + end)) + +(defun py-in-comment-p () + "Return the beginning of current line's comment, if inside. " + (save-restriction + (widen) + (let* ((pps (syntax-ppss)) + (erg (when (nth 4 pps) (nth 8 pps)))) + (unless erg + (when (looking-at (concat "^[ \t]*" comment-start-skip)) + (setq erg (point)))) + erg))) + +(defun py-in-triplequoted-string-p () + "Returns character address of start tqs-string, nil if not inside. " + (interactive) + (let* ((pps (syntax-ppss)) + (erg (when (and (nth 3 pps) (nth 8 pps))(nth 2 pps)))) + (save-excursion + (unless erg (setq erg + (progn + (when (looking-at "\"\"\"\\|''''") + (goto-char (match-end 0)) + (setq pps (syntax-ppss)) + (when (and (nth 3 pps) (nth 8 pps)) (nth 2 pps))))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-in-string-p () + "Returns character address of start of string, nil if not inside. " + (interactive) + (let* ((pps (syntax-ppss)) + (erg (when (nth 3 pps) (nth 8 pps)))) + (save-excursion + (unless erg (setq erg + (progn + (when (looking-at "\"\\|'") + (forward-char 1) + (setq pps (syntax-ppss)) + (when (nth 3 pps) (nth 8 pps))))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-in-statement-p () + "Returns list of beginning and end-position if inside. + +Result is useful for booleans too: (when (py-in-statement-p)...) +will work. +" + (interactive) + (let ((orig (point)) + beg end erg) + (save-excursion + (setq end (py-end-of-statement)) + (setq beg (py-beginning-of-statement)) + (when (and (<= beg orig)(<= orig end)) + (setq erg (cons beg end)) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)))) + +;;; Beginning-of- p +(defun py-beginning-of-line-p () + "Returns position, if cursor is at the beginning of a line, nil otherwise. " + (when (bolp)(point))) + +(defun py-beginning-of-buffer-p () + "Returns position, if cursor is at the beginning of buffer, nil otherwise. " + (when (bobp)(point))) + +(defun py-beginning-of-paragraph-p () + "Returns position, if cursor is at the beginning of a paragraph, nil otherwise. " + (let ((orig (point)) + erg) + (if (and (bolp) (looking-at paragraph-separate)) + (setq erg (point)) + (save-excursion + (py-end-of-paragraph) + (py-beginning-of-paragraph) + (when (eq orig (point)) + (setq erg orig))) + erg))) + +(defun py-beginning-of-statement-p () + "Returns position, if cursor is at the beginning of a statement, nil otherwise. " + (let ((orig (point)) + erg) + (save-excursion + (py-end-of-statement) + (py-beginning-of-statement) + (when (eq orig (point)) + (setq erg orig)) + erg))) + +(defun py-beginning-of-expression-p () + "Returns position, if cursor is at the beginning of a expression, nil otherwise. " + (let ((orig (point)) + erg) + (save-excursion + (py-end-of-expression) + (py-beginning-of-expression) + (when (eq orig (point)) + (setq erg orig)) + erg))) + +(defun py-beginning-of-partial-expression-p () + "Returns position, if cursor is at the beginning of a partial-expression, nil otherwise. " + (let ((orig (point)) + erg) + (save-excursion + (py-end-of-partial-expression) + (py-beginning-of-partial-expression) + (when (eq orig (point)) + (setq erg orig)) + erg))) + +(defun py-beginning-of-block-p () + "Returns position, if cursor is at the beginning of a block, nil otherwise. " + (when (and (looking-at py-block-re) + (not (py-in-string-or-comment-p))) + (point))) + +(defun py-beginning-of-clause-p () + "Returns position, if cursor is at the beginning of a clause, nil otherwise. " + (when (and (looking-at py-clause-re) + (not (py-in-string-or-comment-p))) + (point))) + +(defun py-beginning-of-block-or-clause-p () + "Returns position, if cursor is at the beginning of a block-or-clause, nil otherwise. " + (when (and (looking-at py-block-or-clause-re) + (not (py-in-string-or-comment-p))) + (point))) + +(defun py-beginning-of-def-p () + "Returns position, if cursor is at the beginning of a def, nil otherwise. " + (when (and (looking-at py-def-re) + (not (py-in-string-or-comment-p))) + (point))) + +(defun py-beginning-of-class-p () + "Returns position, if cursor is at the beginning of a class, nil otherwise. " + (when (and (looking-at py-class-re) + (not (py-in-string-or-comment-p))) + (point))) + +(defun py-beginning-of-def-or-class-p () + "Returns position, if cursor is at the beginning of a def-or-class, nil otherwise. " + (when (and (looking-at py-def-or-class-re) + (not (py-in-string-or-comment-p))) + (point))) + +;;; Opens- p +(defun py-statement-opens-block-p (&optional regexp) + "Return position if the current statement opens a block +in stricter or wider sense. + +For stricter sense specify regexp. " + (interactive) + (let* ((regexp (or regexp py-block-re)) + (erg (py-statement-opens-base regexp))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-statement-opens-base (regexp) + (let ((orig (point)) + erg) + (save-excursion + (back-to-indentation) + (py-end-of-statement) + (py-beginning-of-statement) + (when (and + (looking-back "^[ \t]*") (<= (line-beginning-position)(point))(looking-at regexp)) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-statement-opens-clause-p () + "Return position if the current statement opens block or clause. " + (interactive) + (py-statement-opens-base py-clause-re)) + +(defun py-statement-opens-block-or-clause-p () + "Return position if the current statement opens block or clause. " + (interactive) + (py-statement-opens-base py-block-or-clause-re)) + +(defun py-statement-opens-class-p () + "Return `t' if the statement opens a functions or class definition, nil otherwise. " + (interactive) + (py-statement-opens-base py-class-re)) + +(defun py-statement-opens-def-p () + "Return `t' if the statement opens a functions or class definition, nil otherwise. " + (interactive) + (py-statement-opens-base py-def-re)) + +(defun py-statement-opens-def-or-class-p () + "Return `t' if the statement opens a functions or class definition, nil otherwise. " + (interactive) + (py-statement-opens-base py-def-or-class-re)) + +(defun py-statement-closes-block-p () + "Return t iff the current statement closes a block. +I.e., if the line starts with `return', `raise', `break', `continue', +and `pass'. This doesn't catch embedded statements." + (let ((here (point))) + (unless (py-beginning-of-statement-p) (py-beginning-of-statement)) + (prog1 + (looking-at py-block-closing-keywords-re) + (goto-char here)))) + +(defun py-end-base (regexp &optional orig) + "Used internal by functions going to the end forms. " + (let ((orig (or orig (point))) + (erg (if (py-statement-opens-block-p regexp) + (point) + (py-go-to-keyword regexp) + (when (py-statement-opens-block-p regexp) + (point)))) + ind) + (if erg + (progn + (setq ind (+ py-indent-offset (current-indentation))) + (py-end-of-statement) + (forward-line 1) + (setq erg (py-travel-current-indent ind))) + (py-look-downward-for-beginning regexp) + (unless (eobp)(py-end-base regexp orig))) + (if (< orig (point)) + (setq erg (point)) + (setq erg (py-look-downward-for-beginning regexp)) + (when erg (py-end-base regexp orig))) + erg)) + +(defun py-look-downward-for-beginning (regexp) + "When above any beginning of FORM, search downward. " + (let ((erg (re-search-forward regexp nil (quote move) 1))) + (if (and erg (not (py-in-string-or-comment-p)) + (not (py-in-list-p))) + erg + (unless (eobp) + (py-look-downward-for-beginning regexp))))) + +(defun py-current-defun (&optional iact) + "Go to the outermost method or class definition in current scope. + +Python value for `add-log-current-defun-function'. +This tells add-log.el how to find the current function/method/variable. +Returns name of class or methods definition, if found, nil otherwise. + +See customizable variables `py-current-defun-show' and `py-current-defun-delay'." + (interactive "p") + (save-restriction + (widen) + (save-excursion + (let ((erg (when (py-beginning-of-def-or-class) + (forward-word 1) + (skip-chars-forward " \t") + (prin1-to-string (symbol-at-point))))) + (when (and erg py-current-defun-show (push-mark (point) t t) (skip-chars-forward "^ (") + (exchange-point-and-mark) + (sit-for py-current-defun-delay))) + (when iact (message (prin1-to-string erg))) + erg)))) + +(defun py-outdent-p () + "Returns non-nil if the current line should dedent one level." + (save-excursion + (and (progn (back-to-indentation) + (looking-at py-clause-re)) + ;; short circuit infloop on illegal construct + (not (bobp)) + (progn (forward-line -1) + (py-beginning-of-statement) + (back-to-indentation) + (when (looking-at py-blank-or-comment-re) + (backward-to-indentation 1)) + (not (looking-at py-no-outdent-re)))))) + +(defun py-sort-imports () + "Sort multiline imports. + +Put point inside the parentheses of a multiline import and hit +\\[py-sort-imports] to sort the imports lexicographically" + (interactive) + (save-excursion + (let ((open-paren (save-excursion (progn (up-list -1) (point)))) + (close-paren (save-excursion (progn (up-list 1) (point)))) + sorted-imports) + (goto-char (1+ open-paren)) + (skip-chars-forward " \n\t") + (setq sorted-imports + (sort + (delete-dups + (split-string (buffer-substring + (point) + (save-excursion (goto-char (1- close-paren)) + (skip-chars-backward " \n\t") + (point))) + ", *\\(\n *\\)?")) + ;; XXX Should this sort case insensitively? + 'string-lessp)) + ;; Remove empty strings. + (delete-region open-paren close-paren) + (goto-char open-paren) + (insert "(\n") + (insert (py-join-words-wrapping (remove "" sorted-imports) "," " " 78)) + (insert ")")))) + +(defun py-in-literal (&optional lim) + "Return non-nil if point is in a Python literal (a comment or string). +Optional argument LIM indicates the beginning of the containing form, +i.e. the limit on how far back to scan." + (let* ((lim (or lim (point-min))) + (state (syntax-ppss))) + (cond + ((nth 3 state) 'string) + ((nth 4 state) 'comment)))) + +(defun py-which-function () + "Return the name of the function or class, if curser is in, return nil otherwise. " + (interactive) + (save-excursion + (save-restriction + (widen) + (let ((orig (point)) + (erg (if (and (looking-at (concat py-def-or-class-re " +\\([^(]+\\)(.+")) (not (py-in-string-or-comment-p))) + (match-string-no-properties 2) + (progn + (py-beginning-of-def-or-class) + (when (looking-at (concat py-def-or-class-re " +\\([^(]+\\)(.+")) + (match-string-no-properties 2)))))) + (if (and erg (< orig (py-end-of-def-or-class))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + (setq erg nil) + (when (and py-verbose-p (interactive-p)) (message "%s" "Not inside a function or class")) + erg))))) + +(defconst py-help-address "python-mode@python.org" + "List dealing with usage and developing python-mode. + +Also accepts submission of bug reports, whilst a ticket at +http://launchpad.net/python-mode +is preferable for that. ") + +;;; Beg-end forms +(defun py-beginning-of-block (&optional indent) + "Returns beginning of block if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-block-re indent))))) + erg)) + +(defun py-end-of-block () + "Go to the end of block. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-block-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-clause (&optional indent) + "Returns beginning of clause if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-clause-re indent))))) + erg)) + +(defun py-end-of-clause () + "Go to the end of clause. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-clause-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-block-or-clause (&optional indent) + "Returns beginning of block-or-clause if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-block-or-clause-re indent))))) + erg)) + +(defun py-end-of-block-or-clause () + "Go to the end of block-or-clause. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-block-or-clause-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-def (&optional indent) + "Returns beginning of def if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-def-re indent))))) + erg)) + +(defun py-end-of-def () + "Go to the end of def. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-def-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-class (&optional indent) + "Returns beginning of class if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-class-re indent))))) + erg)) + +(defun py-end-of-class () + "Go to the end of class. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-class-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-def-or-class (&optional indent) + "Returns beginning of def-or-class if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-def-or-class-re indent))))) + erg)) + +(defun py-end-of-def-or-class () + "Go to the end of def-or-class. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (save-restriction + (widen) + (let* ((orig (point)) + (pps (syntax-ppss)) + ;; (origline (py-count-lines)) + (erg (if (and (not (nth 8 pps)) (looking-at py-def-or-class-re)) + (point) + (py-go-to-keyword py-def-or-class-re) + (when (and (not (nth 8 pps)) (looking-at py-def-or-class-re)) (point)))) + ind) + (if erg + (progn + (setq ind + (+ (if py-smart-indentation + (save-excursion + (goto-char orig) + ;; (setq origline (py-count-lines)) + (py-end-of-statement) + (py-end-of-statement) + ;; (when (eq origline (py-count-lines)) (py-end-of-statement)) + (py-guess-indent-offset nil (point))) + py-indent-offset) + (current-indentation))) + (py-end-of-statement) + (forward-line 1) + (setq erg (py-travel-current-indent ind))) + (py-look-downward-for-beginning py-def-or-class-re) + (unless (eobp) + ;; (py-end-base py-def-or-class-re orig) + (progn + (setq ind + (+ (if py-smart-indentation + (save-excursion + (goto-char orig) + ;; (setq origline (py-count-lines)) + (py-end-of-statement) + (py-end-of-statement) + ;; (when (eq origline (py-count-lines)) (py-end-of-statement)) + (py-guess-indent-offset nil (point))) + py-indent-offset) + (current-indentation))) + (py-end-of-statement) + (forward-line 1) + (setq erg (py-travel-current-indent ind))) + )) + (if (< orig (point)) + (setq erg (point)) + (setq erg (py-look-downward-for-beginning py-def-or-class-re)) + (when erg + (progn + (setq ind (+ py-indent-offset (current-indentation))) + (py-end-of-statement) + (forward-line 1) + (setq erg (py-travel-current-indent ind))) + ;; (py-end-base py-def-or-class-re orig) + )) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-beginning-of-if-block (&optional indent) + "Returns beginning of if-block if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-if-re indent))))) + erg)) + +(defun py-end-of-if-block () + "Go to the end of if-block. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-if-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-try-block (&optional indent) + "Returns beginning of try-block if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-try-block-re indent))))) + erg)) + +(defun py-end-of-try-block () + "Go to the end of try-block. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-try-block-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-beginning-of-minor-block (&optional indent) + "Returns beginning of minor-block if successful, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let ((erg (ignore-errors (cdr (py-go-to-keyword py-minor-block-re indent))))) + erg)) + +(defun py-end-of-minor-block () + "Go to the end of minor-block. + +Returns position reached, if any, nil otherwise. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html" + (interactive) + (let* ((orig (point)) + (erg (py-end-base py-minor-block-re orig))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +;; Buffer +(defun py-beginning-of-buffer () + "Go to beginning-of-buffer, return position. " + (let ((erg (unless (bobp) + (goto-char (point-min))))) + erg)) + +(defun py-end-of-buffer () + "Go to end-of-buffer, return position. + +If already at end-of-buffer and not at EOB, go to end of next line. " + (let ((erg (unless (eobp) + (goto-char (point-max))))) + erg)) + +(defalias 'py-forward-block 'py-end-of-block) +(defalias 'py-forward-block-or-clause 'py-end-of-block-or-clause) +(defalias 'py-forward-class 'py-end-of-class) +(defalias 'py-forward-clause 'py-end-of-clause) +(defalias 'end-of-def-or-class 'py-end-of-def-or-class) +(defalias 'py-forward-def-or-class 'py-end-of-def-or-class) +(defalias 'py-previous-block 'py-beginning-of-block) +(defalias 'py-goto-block-up 'py-beginning-of-block) +(defalias 'py-backward-block 'py-beginning-of-block) +(defalias 'py-previous-block-or-clause 'py-beginning-of-block-or-clause) +(defalias 'py-goto-block-or-clause-up 'py-beginning-of-block-or-clause) +(defalias 'py-backward-block-or-clause 'py-beginning-of-block-or-clause) +(defalias 'beginning-of-class 'py-beginning-of-class) +(defalias 'py-backward-class 'py-beginning-of-class) +(defalias 'py-previous-class 'py-beginning-of-class) +(defalias 'py-previous-clause 'py-beginning-of-clause) +(defalias 'py-goto-clause-up 'py-beginning-of-clause) +(defalias 'py-backward-clause 'py-beginning-of-clause) +(defalias 'py-backward-def-or-class 'py-beginning-of-def-or-class) +(defalias 'py-previous-def-or-class 'py-beginning-of-def-or-class) + +;;; Expression +(defalias 'py-backward-expression 'py-beginning-of-expression) +(defun py-beginning-of-expression (&optional orig done) + "Go to the beginning of a compound python expression. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +If already at the beginning or before a expression, go to next expression in buffer upwards + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes. +" + (interactive) + (save-restriction + (widen) + (unless (bobp) + (let ((orig (or orig (point))) + (cui (current-indentation)) + (pps (syntax-ppss)) + erg) + (cond + ;; if in string + ((and (nth 3 pps)(nth 8 pps) + (goto-char (nth 8 pps))) + (setq done t) + (unless (looking-back "\\(=\\|:\\|+\\|-\\|*\\|/\\|//\\|&\\|%\\||\\|\^\\|>>\\|<<\\)[ \t]*") + (when (nth 2 pps) + (goto-char (nth 2 pps)))) + (py-beginning-of-expression orig done)) + ;; comments left, as strings are done + ((nth 8 pps) + (goto-char (1- (nth 8 pps))) + (py-beginning-of-expression orig done)) + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*")) + (forward-line -1) + (unless (bobp) + (end-of-line) + (py-beginning-of-expression orig done))) + ;; character address of start of innermost containing list; nil if none. + ((nth 1 pps) + (goto-char (nth 1 pps)) + (when + (not (looking-back "[ \t]+")) + (when (< 0 (abs (skip-chars-backward py-expression-skip-regexp))) + (setq done t))) + (py-beginning-of-expression orig done)) + ((looking-at "\\(=\\|:\\|+\\|-\\|*\\|/\\|//\\|&\\|%\\||\\|\^\\|>>\\|<<\\)") + (goto-char (1- (match-beginning 0))) + (skip-chars-backward " \t\r\n\f") + (forward-char -1) + (py-beginning-of-expression orig done)) + ((looking-back "[\])}]") + (forward-char -1) + (py-beginning-of-expression orig done)) + + ;; inside expression + ((and (eq (point) orig) (not (bobp)) (looking-back py-expression-looking-regexp)) + (skip-chars-backward py-expression-skip-regexp) + (setq done t) + (py-beginning-of-expression orig done)) + ((looking-at "[ \t\r\n\f]") + (skip-chars-backward " \t\r\n\f") + (unless (bobp) (forward-char -1)) + (unless (eq (abs (skip-chars-backward "^ \t\r\n\f")) 0) + (py-beginning-of-expression orig done))) + ((and (eq (point) orig) (not (bobp))(looking-back "[ \.\t\r\n\f]")) + (skip-chars-backward "=:+-*/&%^><. \t\r\n\f") + (unless (bobp) (forward-char -1)) + (unless (eq (abs (skip-chars-backward "^=:+-*/&%^>< \t\r\n\f")) 0) + (setq done t) + (py-beginning-of-expression orig done))) + ((and (eq (point) orig) (not (bobp)) (looking-back py-expression-looking-regexp)) + (forward-char -1) + (when (< 0 (abs (skip-chars-backward py-expression-skip-regexp))) + (setq done t)) + (py-beginning-of-expression orig done)) + ((looking-at py-expression-looking-regexp) + (setq done t))) + (unless (or (eq (point) orig)(looking-at "[ \t]*#")) + (setq erg (point))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)))) + +(defun py-end-of-expression (&optional orig done) + "Go to the end of a compound python expression. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference + +Operators however are left aside resp. limit py-expression designed for edit-purposes. " + (interactive) + (save-restriction + (widen) + (unless (eobp) + (let* + ((orig (or orig (point))) + (pps (syntax-ppss)) + (done done) + erg + ;; use by scan-lists + parse-sexp-ignore-comments) + (cond + ;; start of innermost containing list; nil if none. + ((nth 1 pps) + (goto-char (nth 1 pps)) + (let ((parse-sexp-ignore-comments t)) + (forward-list) + (py-end-of-expression orig done))) + ;; in comment + ((nth 4 pps) + (or (< (point) (progn (forward-comment 1)(point)))(forward-line 1)) + (py-end-of-expression orig done)) + ((and (empty-line-p)(not done)(not (eobp))) + (while + (and (empty-line-p)(not done)(not (eobp))) + (forward-line 1)) + (py-end-of-expression orig done)) + ;; inside string + ((py-in-string-p) + (when (looking-at "\"\"\"\\|'''\\|\"\\|'") + (goto-char (match-end 0))) + (while + (nth 3 (syntax-ppss)) + (forward-char 1)) + (when (looking-at "[ \t]*$") + (setq done t)) + (py-end-of-expression orig done)) + ((and (looking-at "[ \t]*#")(looking-back "^[ \t]*")(not done)) + (while (and (looking-at "[ \t]*#") (forward-line 1)(not (eobp)) + (beginning-of-line))) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (unless (bolp) (setq done t)) + (py-end-of-expression orig done)) + ((and (not done)(looking-at py-not-expression-regexp)(not (eobp))) + (goto-char (match-end 0)) + (skip-chars-forward " \t\r\n\f") + (py-end-of-expression orig done)) + ((and (not done)(looking-at py-expression-skip-regexp)(not (eobp))) + (skip-chars-forward py-not-expression-regexp) + (forward-char -1) + (py-end-of-expression orig done)) + ((and + ;; (not done) + (not (eobp))(looking-at py-expression-looking-regexp)) + (goto-char (match-end 0)) + (setq done t) + (unless (< 0 (skip-chars-forward py-expression-skip-regexp)) + (unless (looking-at "[ \t\r\n\f]") (forward-char 1))) + (py-end-of-expression orig done)) + ((looking-at "\\(+\\|-\\|*\\|/\\|//\\|&\\|%\\||\\|\^\\|>>\\|<<\\)[ \t]*") + (setq done t) + (goto-char (match-end 0)) + (py-end-of-expression orig done))) + (unless (eq (point) orig) + (setq erg (point))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)))) + +;;; Partial- or Minor Expression +(defalias 'py-backward-partial-expression 'py-beginning-of-partial-expression) +(defun py-beginning-of-partial-expression (&optional orig erg) + "Go to the beginning of a minor python expression. + +\".\" operators delimit a minor expression on their level. +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes. + +If already at the beginning or before a partial-expression, go to next partial-expression in buffer upwards " + (interactive) + (save-restriction + (widen) + (unless (bobp) + (let ((orig (or orig (point))) + (cui (current-indentation)) + ;; (origline (or origline (py-count-lines))) + (pps (syntax-ppss)) + (erg erg)) + (cond + ((nth 1 pps) + (goto-char (nth 1 pps)) + ;; (skip-chars-backward py-partial-expression-backward-regexp) + (setq erg (point))) + ((nth 8 pps) + (when (nth 2 pps) + (goto-char (nth 2 pps))) + (goto-char (1- (nth 8 pps))) + (setq erg (point)) + (py-beginning-of-partial-expression orig erg)) + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*")) + (forward-line -1) + (unless (bobp) + (end-of-line) + (setq erg (point)) + (py-beginning-of-partial-expression orig erg))) + + ((looking-at "\\(=\\|:\\|+\\|-\\|*\\|/\\|//\\|&\\|%\\||\\|\^\\|>>\\|<<\\)") + (goto-char (1- (match-beginning 0))) + (skip-chars-backward " \t\r\n\f") + (unless (bobp) (forward-char -1)) + (py-beginning-of-partial-expression orig erg)) + ((looking-at "[ \t\r\n\f]") + (skip-chars-backward " \t\r\n\f") + (unless (bobp) (forward-char -1)) + (unless (eq (abs (skip-chars-backward "^ .\t\r\n\f")) 0) + (py-beginning-of-partial-expression orig erg))) + ((and (eq (point) orig) (not (bobp))(looking-back "[ \.\t\r\n\f]")) + (skip-chars-backward "=:+-*/&%^><. \t\r\n\f") + (unless (bobp) (forward-char -1)) + (unless (eq (abs (skip-chars-backward "^=:+-*/&%^><. \t\r\n\f")) 0) + (py-beginning-of-partial-expression orig erg))) + ((and (eq (point) orig) (not (bobp)) (looking-back py-partial-expression-looking-regexp)) + (forward-char -1) + (when (< 0 (abs (skip-chars-backward py-partial-expression-skip-regexp))) + (setq erg (point))) + (py-beginning-of-partial-expression orig erg)) + ((looking-at py-partial-expression-looking-regexp) + (setq erg (point))) + ((and (eq (point) orig)(not (bobp))) + (skip-chars-backward " \t\r\n\f") + (py-beginning-of-partial-expression orig erg)) + (t (unless (and (looking-at "[ \t]*#") (looking-back "^[ \t]*"))(setq erg (point))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)))) + +(defalias 'py-forward-partial-expression 'py-end-of-partial-expression) +(defun py-end-of-partial-expression (&optional orig origline done) + "Go to the end of a minor python expression. + +\".\" operators delimit a minor expression on their level. +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes. " + (interactive) + (save-restriction + (widen) + (unless (eobp) + (let* + ((orig (or orig (point))) + (origline (or origline (py-count-lines))) + (pps (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss))) + (done done) + erg + ;; use by scan-lists + parse-sexp-ignore-comments) + (cond + ((and (empty-line-p)(not done)(not (eobp))) + (while + (and (empty-line-p)(not done)(not (eobp))) + (forward-line 1)) + (py-end-of-partial-expression orig origline done)) + ;; inside string + ((nth 3 pps) + (when (looking-at "\"\"\"\\|'''\\|\"\\|'") + (goto-char (match-end 0))) + (while + (and (re-search-forward "[^\\]\"\"\"\\|[^\\]'''\\|[^\\]\"\\|[^\\]'" nil (quote move) 1) + (nth 3 + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss))))) + (py-end-of-partial-expression orig origline done)) + ;; in comment + ((nth 4 pps) + (forward-line 1) + (py-end-of-partial-expression orig origline done)) + ((and (looking-at "[ \t]*#")(looking-back "^[ \t]*")(not done)) + (while (and (looking-at "[ \t]*#") (forward-line 1)(not (eobp)) + (beginning-of-line))) + (end-of-line) + ;; (setq done t) + (skip-chars-backward " \t\r\n\f") + (py-end-of-partial-expression orig origline done)) + ((and (nth 1 pps) (<= orig (nth 1 pps))) + (goto-char (nth 1 pps))(forward-list) + (point)) + ((and (not done)(ignore-errors (<= orig (nth 2 pps)))) + (goto-char (nth 2 pps)) + (setq done t) + (skip-chars-forward py-partial-expression-forward-regexp) + (py-end-of-partial-expression orig origline done)) + ((and (looking-at "\\.")(< orig (point))) + (point)) + ((and (not done)(looking-at "\\.\\|=\\|:\\|+\\|-\\|*\\|/\\|//\\|&\\|%\\||\\|\^\\|>>\\|<<\\|<\\|<=\\|>\\|>=\\|==\\|!=")) + (goto-char (match-end 0)) + (when (< 0 (skip-chars-forward " \t\r\n\f")) + (forward-char 1)) + (skip-chars-forward py-partial-expression-forward-regexp) + (setq done t) + (py-end-of-partial-expression orig origline done)) + ((and (not done)(looking-at py-partial-expression-looking-regexp)(not (eobp))) + (skip-chars-forward py-partial-expression-forward-regexp) + (setq done t) + (py-end-of-partial-expression orig origline done)) + ((and (not done)(looking-at py-not-partial-expression-regexp)(not (eobp))) + (skip-chars-forward py-not-partial-expression-skip-regexp) + (skip-chars-forward py-partial-expression-forward-regexp) + (setq done t) + (py-end-of-partial-expression orig origline done)) + ((and (eq (point) orig) (not (eobp))) + (forward-char 1) + (py-end-of-partial-expression orig origline done))) + (unless (eq (point) orig) + (setq erg (point))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)))) + +;;; Line +(defun py-beginning-of-line () + "Go to beginning-of-line, return position. + +If already at beginning-of-line and not at BOB, go to beginning of previous line. " + (interactive) + (let ((erg (unless (bobp) + (if (bolp) + (progn + (forward-line -1) + (progn (beginning-of-line)(point))) + (progn (beginning-of-line)(point)))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-end-of-line () + "Go to end-of-line, return position. + +If already at end-of-line and not at EOB, go to end of next line. " + (interactive) + (let ((erg (unless (eobp) + (if (eolp) + (progn + (forward-line 1) + (progn (end-of-line)(point))) + (progn (end-of-line)(point)))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +;;; Statement +(defalias 'py-backward-statement 'py-beginning-of-statement) +(defalias 'py-previous-statement 'py-beginning-of-statement) +(defalias 'py-statement-backward 'py-beginning-of-statement) +(defun py-beginning-of-statement (&optional orig done) + "Go to the initial line of a simple statement. + +For beginning of compound statement use py-beginning-of-block. +For beginning of clause py-beginning-of-clause. + +Referring python program structures see for example: +http://docs.python.org/reference/compound_stmts.html +" + (interactive) + (unless (bobp) + (let ((orig (or orig (point))) + (cui (current-indentation)) + (pps (syntax-ppss)) + (done done) + erg) + (cond + ((empty-line-p) + (skip-chars-backward " \t\r\n\f") + (py-beginning-of-statement orig done)) + ((nth 8 pps) + (goto-char (1- (nth 8 pps))) + (setq done t) + (py-beginning-of-statement orig done)) + ((nth 1 pps) + (goto-char (1- (nth 1 pps))) + (setq done t) + (py-beginning-of-statement orig done)) + ((and (not done) (not (eq 0 (skip-chars-backward " \t\r\n\f")))) + (setq done t) + (py-beginning-of-statement orig done)) + ((not (eq (current-column) (current-indentation))) + (back-to-indentation) + (setq done t) + (py-beginning-of-statement orig done)) + ((looking-at "[ \t]*#") + (skip-chars-backward " \t\r\n\f") + (setq done t) + (py-beginning-of-statement orig done)) + ((py-continuation-line-p) + (forward-line -1) + (setq done t) + (py-beginning-of-statement orig done))) + (unless (and (looking-at "[ \t]*#") (looking-back "^[ \t]*")) + (when (< (point) orig)(setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-go-to-keyword (regexp &optional maxindent) + "Returns a list, whose car is indentation, cdr position. " + (let ((orig (point)) + (origline (py-count-lines)) + (maxindent maxindent) + done erg cui) + (while (and (not done) (not (bobp))) + (py-beginning-of-statement) + (when (and (looking-at regexp)(if maxindent + (< (current-indentation) maxindent)t)) + (setq erg (point)) + (setq done t))) + (when erg (setq erg (cons (current-indentation) erg))) + erg)) + +;; (defmacro py-go-to-keyword (regexp &optional maxindent) +;; "Returns a list, whose car is indentation, cdr position. " +;; `(let ((orig (point)) +;; (origline (py-count-lines)) +;; (maxindent maxindent) +;; done erg) +;; (while (and (not done) (not (bobp))) +;; (py-beginning-of-statement) +;; (when (and (looking-at ,regexp)(if maxindent +;; (< (current-indentation) maxindent)t)) +;; (setq erg (point)) +;; (setq done t))) +;; (when erg (cons (current-indentation) erg)))) + +(defalias 'py-statement-forward 'py-end-of-statement) +(defalias 'py-next-statement 'py-end-of-statement) +(defalias 'py-forward-statement 'py-end-of-statement) +(defun py-end-of-statement (&optional orig done) + "Go to the last char of current statement. + +To go just beyond the final line of the current statement, use `py-down-statement-lc'. " + (interactive) + (unless (eobp) + (let ((pps (syntax-ppss)) + (orig (point)) + ;; use by scan-lists + parse-sexp-ignore-comments erg) + (cond + ((and (not done) (< 0 (skip-chars-forward " \t\r\n\f"))) + (end-of-line) + (py-beginning-of-comment) + (skip-chars-backward " \t\r\n\f" (line-beginning-position)) + (if (eq (point) orig) + (progn + (end-of-line) + (forward-comment 99999) + (py-end-of-statement orig done)) + (setq done t) + (py-end-of-statement orig done))) + ((nth 1 pps) + (when (< orig (point)) + (setq orig (point))) + (goto-char (nth 1 pps)) + (let ((parse-sexp-ignore-comments t)) + (if (ignore-errors (forward-list)) + (progn + (when (looking-at ":[ \t]*$") + (forward-char 1)) + (setq done t) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (py-end-of-statement orig done)) + (goto-char orig)))) + ((and (nth 8 pps)(nth 3 pps)) + (goto-char (nth 8 pps)) + (if (looking-at "\"\"\"\\|'''") + (goto-char (match-end 0)) + (forward-char 1)) + (while (and (re-search-forward (match-string-no-properties 0) nil (quote move) 1) + (nth 3 (syntax-ppss)))) + (setq done t) + (end-of-line) + (skip-chars-backward " \t\r\n\f" (line-beginning-position)) + (py-end-of-statement orig done)) + ;; in comment + ((nth 4 pps) + (if (eobp) + nil + (setq done t) + (forward-comment 99999) + (end-of-line) + (skip-chars-backward " \t\r\n\f" (line-beginning-position)) + (py-beginning-of-comment) + (skip-chars-backward " \t\r\n\f") + (py-end-of-statement orig done))) + ((looking-at "#") + ;; (skip-chars-forward "#") + (end-of-line) + (forward-comment 99999) + (setq done t) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (py-beginning-of-comment) + (skip-chars-backward " \t\r\n\f") + (py-end-of-statement orig done)) + ((py-current-line-backslashed-p) + (skip-chars-forward " \t\r\n\f") + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (py-beginning-of-comment) + (skip-chars-backward " \t\r\n\f") + (setq done t) + (py-end-of-statement orig done)) + ((and (not done) (eq (point) orig)) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (py-beginning-of-comment) + (skip-chars-backward " \t\r\n\f") + (setq done t) + (py-end-of-statement orig done))) + (unless + (or + (eq (point) orig) + (eq 0 (current-column))) + (setq erg (point))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-goto-statement-below () + "Goto beginning of next statement. " + (interactive) + (let ((orig (point)) + (erg (py-end-of-statement))) + (py-beginning-of-statement) + (when (< (point) orig) + (goto-char erg) + (py-end-of-statement) + (py-beginning-of-statement)))) + +;;; Mark forms +(defun py-mark-base (form &optional py-mark-decorators) + (let* ((begform (intern-soft (concat "py-beginning-of-" form))) + (endform (intern-soft (concat "py-end-of-" form))) + (begcheckform (intern-soft (concat "py-beginning-of-" form "-p"))) + (orig (point)) + beg end erg) + (setq beg (if + (setq beg (funcall begcheckform)) + beg + (funcall begform))) + (when py-mark-decorators + (save-excursion + (when (setq erg (py-beginning-of-decorator)) + (setq beg erg)))) + (setq end (funcall endform)) + (push-mark beg t t) + (unless end (when (< beg (point)) + (setq end (point)))) + (when (interactive-p) (message "%s %s" beg end)) + (cons beg end))) + +(defun py-mark-paragraph () + "Mark paragraph at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "paragraph")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-block () + "Mark block at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "block")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-clause () + "Mark clause at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "clause")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-block-or-clause () + "Mark block-or-clause at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "block-or-clause")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-def (&optional arg) + "Mark def at point. + +With \\[universal argument] or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons. " + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + erg) + (py-mark-base "def" py-mark-decorators) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-class (&optional arg) + "Mark class at point. + +With \\[universal argument] or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons. " + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + erg) + (py-mark-base "class" py-mark-decorators) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-def-or-class (&optional arg) + "Mark def-or-class at point. + +With \\[universal argument] or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons. " + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + erg) + (py-mark-base "def-or-class" py-mark-decorators) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-line () + "Mark line at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "line")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-statement () + "Mark statement at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "statement")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-expression () + "Mark expression at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "expression")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-mark-partial-expression () + "Mark partial-expression at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let (erg) + (setq erg (py-mark-base "partial-expression")) + (exchange-point-and-mark) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +;;; Decorator +(defun py-beginning-of-decorator () + "Go to the beginning of a decorator. + +Returns position if succesful " + (interactive) + (back-to-indentation) + (while (and (not (looking-at "@\\w+"))(not (empty-line-p))(not (bobp))(forward-line -1)) + (back-to-indentation)) + (let ((erg (when (looking-at "@\\w+")(match-beginning 0)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-end-of-decorator () + "Go to the end of a decorator. + +Returns position if succesful " + (interactive) + (let ((orig (point)) erg) + (unless (looking-at "@\\w+") + (setq erg (py-beginning-of-decorator))) + (when erg + (if + (re-search-forward py-def-or-class-re nil t) + (progn + (back-to-indentation) + (skip-chars-backward " \t\r\n\f") + (py-leave-comment-or-string-backward) + (skip-chars-backward " \t\r\n\f") + (setq erg (point))) + (goto-char orig) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (when (ignore-errors (goto-char (py-in-list-p))) + (forward-list)) + (when (< orig (point)) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +;;; Copying +(defalias 'py-expression 'py-copy-expression) +(defun py-copy-expression () + "Mark expression at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let ((erg (py-mark-base "expression"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defalias 'py-partial-expression 'py-copy-partial-expression) +(defun py-copy-partial-expression () + "Mark partial-expression at point. + +Returns beginning and end positions of marked area, a cons. + +\".\" operators delimit a partial-expression expression on it's level, that's the difference to compound expressions. + +Given the function below, `py-partial-expression' +called at pipe symbol would copy and return: + +def usage(): + print \"\"\"Usage: %s + ....\"\"\" % ( + os.path.basename(sys.argv[0])) +------------|------------------------- +==> path + + os.path.basename(sys.argv[0])) +------------------|------------------- +==> basename(sys.argv[0])) + + os.path.basename(sys.argv[0])) +--------------------------|----------- +==> sys + + os.path.basename(sys.argv[0])) +------------------------------|------- +==> argv[0] + +while `py-expression' would copy and return + +\( + os.path.basename(sys.argv[0])) + +\;;;;; + +Also for existing commands a shorthand is defined: + +\(defalias 'py-statement 'py-copy-statement)" + + (interactive) + (let ((erg (py-mark-base "partial-expression"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-statement 'py-copy-statement) +(defun py-copy-statement () + "Mark statement at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let ((erg (py-mark-base "statement"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-block 'py-copy-block) +(defun py-copy-block () + "Mark block at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let ((erg (py-mark-base "block"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-block-or-clause 'py-copy-block-or-clause) +(defun py-copy-block-or-clause () + "Mark block-or-clause at point. + +Returns beginning and end positions of marked area, a cons. " + (interactive) + (let ((erg (py-mark-base "block-or-clause"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-def 'py-copy-def) +(defun py-copy-def (&optional arg) + "Mark def at point. + +With universal argument or `py-mark-decorators' set to `t' decorators are copied too. +Returns beginning and end positions of marked area, a cons." + + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py-mark-base "def" py-mark-decorators))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defun py-copy-def-or-class (&optional arg) + "Mark def-or-class at point. + +With universal argument or `py-mark-decorators' set to `t' decorators are copied too. +Returns beginning and end positions of marked area, a cons." + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py-mark-base "def-or-class" py-mark-decorators))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-class 'py-copy-class) +(defun py-copy-class (&optional arg) + "Mark class at point. + +With universal argument or `py-mark-decorators' set to `t' decorators are copied too. +Returns beginning and end positions of marked area, a cons." + + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py-mark-base "class" py-mark-decorators))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +(defalias 'py-clause 'py-copy-clause) +(defun py-copy-clause () + "Mark clause at point. + Returns beginning and end positions of marked area, a cons. " + (interactive) + (let ((erg (py-mark-base "clause"))) + (kill-new (buffer-substring-no-properties (car erg) (cdr erg))))) + +;;; Deleting +(defun py-kill-expression () + "Delete expression at point. + Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive) + (let ((erg (py-mark-base "expression"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-partial-expression () + "Delete partial-expression at point. + Stores data in kill ring. Might be yanked back using `C-y'. + +\".\" operators delimit a partial-expression expression on it's level, that's the difference to compound expressions." + (interactive) + (let ((erg (py-mark-base "partial-expression"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-statement () + "Delete statement at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "statement"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-block () + "Delete block at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-block-or-clause () + "Delete block-or-clause at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "block-or-clause"))) + (kill-region (region-beginning) (region-end)))) + +(defun py-kill-def-or-class () + "Delete def-or-class at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "def-or-class"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-class () + "Delete class at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "class"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-def () + "Delete def at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "def"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-clause () + "Delete clause at point. + +Stores data in kill ring. Might be yanked back using `C-y'. " + (interactive "*") + (let ((erg (py-mark-base "clause"))) + (kill-region (car erg) (cdr erg)))) + +;;; Helper functions +(defun py-warn-tmp-files-left () + "Detect and warn about file of form \"py11046IoE\" in py-temp-directory. " + (let ((erg1 (file-readable-p (concat py-temp-directory (char-to-string py-separator-char) (car (directory-files py-temp-directory nil "py[[:alnum:]]+$")))))) + (when (and py-verbose-p erg1) + (message "py-warn-tmp-files-left: %s ?" (concat py-temp-directory (char-to-string py-separator-char) (car (directory-files py-temp-directory nil "py[[:alnum:]]*$"))))))) + +(defun py-forward-line (&optional arg) + "Goes to end of line after forward move. + +Travels right-margin comments. " + (interactive "p") + (let ((arg (or arg 1))) + (forward-line arg) + (end-of-line) + (skip-chars-backward " \t") + (py-beginning-of-comment) + (skip-chars-backward " \t"))) + +(defun py-go-to-beginning-of-comment () + "Go to the beginning of current line's comment, if any. + +From a programm use `py-beginning-of-comment' instead " + (interactive) + (let ((erg (py-beginning-of-comment))) + (when (and py-verbose-p (interactive-p)) + (message "%s" erg)))) + +(defun py-beginning-of-comment () + "Go to the beginning of current line's comment, if any. + +Returns position if succesful. " + (save-restriction + (widen) + (let ((pps + (if (featurep 'xemacs) + (parse-partial-sexp (line-beginning-position) (point)) + (syntax-ppss)))) + (when (nth 4 pps) + (goto-char + (nth 8 pps)))))) + +(defun py-clause-lookup-keyword (regexp arg &optional indent orig origline) + "Returns a list, whose car is indentation, cdr position. " + (let* ((orig (or orig (point))) + (origline (or origline (py-count-lines))) + (stop (if (< 0 arg)'(eobp)'(bobp))) + (function (if (< 0 arg) 'py-end-of-statement 'py-beginning-of-statement)) + (count 1) + (maxindent (cond (indent indent) + ((< (py-count-lines) origline) + (current-indentation)) + (t 0))) + (complement-re + (cond ((or (string-match "finally" regexp) + (string-match "except" regexp)) + py-try-re) + ((string-match "elif" regexp) + py-if-re) + ((string-match "else" regexp) + py-minor-block-re))) + (first t) + erg done strict) + (while (and (not (eval stop)) + (< 0 count) + (or done (setq erg (funcall function)))) + (setq done nil) + (when (and first (< maxindent (current-indentation))) + (setq maxindent (current-indentation)) + (setq first nil)) + (when (if strict + (< (current-indentation) maxindent) + (<= (current-indentation) maxindent)) + (unless (looking-at py-block-or-clause-re) + (setq maxindent (current-indentation))) + ;; (message "%s %s" count indent) + ;; nesting + (cond + ((and (looking-at "\\_<finally\\>[: \n\t]")(save-match-data (string-match regexp "finally"))) + (setq indent (current-indentation)) + (while + (and + (not (eval stop)) + (funcall function) + (setq done t) + (not (and (eq indent (current-indentation)) (looking-at "try")))))) + ;; ((and (looking-at "\\<except\\>[: \n\t]")(save-match-data (string-match "else" regexp))) + ;; (setq indent (current-indentation)) + ;; (setq count (1+ count)) + ;; (while + ;; (and + ;; (not (eval stop)) + ;; (funcall function) + ;; (setq done t) + ;; (not (and (eq indent (current-indentation)) (looking-at "try\\|if")))))) + ((and (looking-at "\\<else\\>[: \n\t]")(save-match-data (string-match "else" regexp))) + (setq indent (current-indentation)) + (setq count (1+ count)) + (while + (and + (not (eval stop)) + (funcall function) + (setq done t) + (not (and (eq indent (current-indentation)) (looking-at "try\\|if")))))) + ((and (looking-at "\\_<else\\>[: \n\t]")(save-match-data (string-match "else" regexp))) + (setq indent (current-indentation)) + (setq count (1+ count)) + (while + (and + (not (eval stop)) + (funcall function) + (setq done t) + (not (and (eq indent (current-indentation)) (looking-at "try\\|if")))))) + ((and (looking-at "\\_<elif\\>[ \n\t]")(save-match-data (string-match "elif" regexp))) + (setq indent (current-indentation)) + (while + (and + (not (eval stop)) + (funcall function) + (setq done t) + ;; doesn't mean nesting yet + (setq count (1- count)) + (not (and (eq indent (current-indentation)) (looking-at "if")))))) + ((and (looking-at complement-re)(<= (current-indentation) maxindent)) + (setq count (1- count))) + (t (cond ((and (string-match "except" regexp)(looking-at py-block-re)) + (setq count (1- count))) + ((and (string-match "else" regexp)(looking-at "except")) + (current-indentation)) + (t + (setq strict t) + )))))) + (when erg + (if (looking-at py-def-or-class-re) + (setq erg (cons (+ (current-indentation) py-indent-offset) erg)) + (setq erg (cons (current-indentation) erg)))) + erg)) + +(defun py-leave-comment-or-string-backward (&optional pos) + "If inside a comment or string, leave it backward. " + (interactive) + (let ((pps + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (syntax-ppss)))) + (when (nth 8 pps) + (goto-char (1- (nth 8 pps)))))) + +(defun py-beginning-of-list-pps (&optional iact last ppstart orig done) + "Go to the beginning of a list. +Optional ARG indicates a start-position for `parse-partial-sexp'. +Return beginning position, nil if not inside." + (interactive "p") + (let* ((orig (or orig (point))) + (ppstart (or ppstart (re-search-backward "^[a-zA-Z]" nil t 1) (point-min))) + erg) + (unless done (goto-char orig)) + (setq done t) + (if + (setq erg (nth 1 (if (featurep 'xemacs) + (parse-partial-sexp ppstart (point)) + (syntax-ppss)))) + (progn + (setq last erg) + (goto-char erg) + (py-beginning-of-list-pps iact last ppstart orig done)) + (when iact (message "%s" last)) + last))) + +(when (featurep 'thing-at-point-utils) + (defun py-beginning-of-list (&optional iact orig limit done last) + "Go to beginning of any parentized, braced or bracketed expression in statement. " + (interactive "p") + (save-restriction + (let ((orig (or orig (point))) + (done done) + (limit (or limit (re-search-backward "^[a-zA-Z]" nil t 1))) + (last last)) + (unless (or done (not limit)) (narrow-to-region limit (point-max))) + (setq done t) + (goto-char orig) + (let* ((pt (car-safe (ar-in-parentized-p-atpt))) + (br (car-safe (ar-in-braced-p-atpt))) + (bk (car-safe (ar-in-bracketed-p-atpt))) + (erg (car (sort (delq nil (list pt br bk)) '<)))) + (if erg + (progn + (goto-char (1- erg)) + (setq last erg) + (py-beginning-of-list iact (1- erg) limit done last)) + (when last + (goto-char last)) + (when iact (message "%s" last)) + last))))) + + (defun py-end-of-list (&optional iact orig limit done last) + "Go to end of any parentized, braced or bracketed expression in statement. " + (interactive "p") + (save-restriction + (let ((orig (or orig (point))) + (done done) + (limit (or limit (re-search-backward "^[a-zA-Z]" nil t 1))) + (last last)) + (unless (or done (not limit)) (narrow-to-region limit (point-max))) + (setq done t) + (goto-char orig) + (let* ((pt (car-safe (ar-in-parentized-p-atpt))) + (br (car-safe (ar-in-braced-p-atpt))) + (bk (car-safe (ar-in-bracketed-p-atpt))) + (erg (car (sort (delq nil (list pt br bk)) '<)))) + (if erg + (progn + (goto-char (1- erg)) + (setq last erg) + (py-end-of-list iact (1- erg) limit done last)) + (when last + (goto-char last) + (match-paren) + (setq last (1+ (point))) + (when iact (message "%s" last)) + last))))))) + +;;; Down +(defun py-down-block-lc () + "Goto beginning of line following end of block. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-block' stops at right corner. + +See also `py-down-block': down from current definition to next beginning of block below. " + (interactive) + (let ((erg (py-end-of-block))) + (when erg + (unless (eobp) + (forward-line 1) + (beginning-of-line) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-clause-lc () + "Goto beginning of line following end of clause. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-clause' stops at right corner. + +See also `py-down-clause': down from current definition to next beginning of clause below. " + (interactive) + (let ((erg (py-end-of-clause))) + (when erg + (unless (eobp) + (forward-line 1) + (beginning-of-line) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-def-lc () + "Goto beginning of line following end of def. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-def' stops at right corner. + +See also `py-down-def': down from current definition to next beginning of def below. " + (interactive) + (let ((erg (py-end-of-def))) + (when erg + (unless (eobp) + (forward-line 1) + (beginning-of-line) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-class-lc () + "Goto beginning of line following end of class. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-class' stops at right corner. + +See also `py-down-class': down from current definition to next beginning of class below. " + (interactive) + (let ((erg (py-end-of-class))) + (when erg + (unless (eobp) + (forward-line 1) + (beginning-of-line) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-statement-lc () + "Goto beginning of line following end of statement. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-statement' stops at right corner. + +See also `py-down-statement': down from current definition to next beginning of statement below. " + (interactive) + (let ((erg (py-end-of-statement))) + (when erg + (unless (eobp) + (forward-line 1) + (beginning-of-line) + (setq erg (point)))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-statement () + "Go to the beginning of next statement below in buffer. + +Returns indentation if statement found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (progn + (when (setq erg (py-end-of-statement)) + (if (< orig (setq erg (py-beginning-of-statement-position))) + (goto-char erg) + (setq erg (py-end-of-statement)) + (when erg + (py-beginning-of-statement)))) + (when erg + (setq erg (current-column))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-block () + "Go to the beginning of next block below in buffer. + +Returns indentation if block found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (re-search-forward py-block-re nil (quote move)) + (nth 8 (if (featurep 'xemacs) + (parse-partial-sexp ppstart (point)) + (syntax-ppss))))) + (back-to-indentation) + (when (looking-at py-block-re) (setq erg (current-indentation))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg))) + +(defun py-down-clause () + "Go to the beginning of next clause below in buffer. + +Returns indentation if clause found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (setq erg (py-down-statement))(not (looking-at py-clause-re))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-block-or-clause () + "Go to the beginning of next block-or-clause below in buffer. + +Returns indentation if block-or-clause found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (setq erg (py-down-statement))(not (looking-at py-block-or-clause-re))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-def () + "Go to the beginning of next def below in buffer. + +Returns indentation if def found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (setq erg (py-down-statement))(not (looking-at py-def-re))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-class () + "Go to the beginning of next class below in buffer. + +Returns indentation if class found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (setq erg (py-down-statement))(not (looking-at py-class-re))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-down-def-or-class () + "Go to the beginning of next def-or-class below in buffer. + +Returns indentation if def-or-class found, nil otherwise. " + (interactive) + (let* ((orig (point)) + erg) + (if (eobp) + (setq erg nil) + (while (and (setq erg (py-down-statement))(not (looking-at py-def-or-class-re))))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) +;; Py-down commands end + +;; ripped from cc-mode +(defun py-forward-into-nomenclature (&optional arg iact) + "Move forward to end of a nomenclature section or word. + +With \\[universal-argument] (programmatically, optional argument ARG), do it that many times. + +A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." + (interactive "p") + (or arg (setq arg 1)) + (let ((case-fold-search nil) + (orig (point)) + erg) + (if (> arg 0) + (while (and (not (eobp)) (> arg 0)) + ;; (setq erg (re-search-forward "\\(\\W+[_[:lower:][:digit:]ß]+\\)" nil t 1)) + (cond + ((or (not (eq 0 (skip-chars-forward "[[:blank:][:punct:]\n\r]"))) + (not (eq 0 (skip-chars-forward "_")))) + (when (or + (< 1 (skip-chars-forward "[:upper:]")) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]ß]"))) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]]")))) + (setq arg (1- arg)))) + ((or + (< 1 (skip-chars-forward "[:upper:]")) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]ß]"))) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]]")))) + (setq arg (1- arg))))) + (while (and (not (bobp)) (< arg 0)) + (when (not (eq 0 (skip-chars-backward "[[:blank:][:punct:]\n\r\f_]"))) + + (forward-char -1)) + (or + (not (eq 0 (skip-chars-backward "[:upper:]"))) + (not (eq 0 (skip-chars-backward "[[:lower:][:digit:]ß]"))) + (skip-chars-backward "[[:lower:][:digit:]ß]")) + (setq arg (1+ arg)))) + (if (< (point) orig) + (progn + (when (looking-back "[[:upper:]]") + ;; (looking-back "[[:blank:]]" + (forward-char -1)) + (if (looking-at "[[:alnum:]ß]") + (setq erg (point)) + (setq erg nil))) + (if (and (< orig (point)) (not (eobp))) + (setq erg (point)) + (setq erg nil))) + (when (and py-report-position-p (or iact (interactive-p))) (message "%s" erg)) + erg)) + +(defun py-backward-into-nomenclature (&optional arg) + "Move backward to beginning of a nomenclature section or word. + +With optional ARG, move that many times. If ARG is negative, move +forward. + +A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." + (interactive "p") + (setq arg (or arg 1)) + (py-forward-into-nomenclature (- arg) arg)) + +(defalias 'py-match-paren 'match-paren) + +(defun match-paren (&optional arg) + "Go to the matching brace, bracket or parenthesis if on its counterpart. + +Otherwise insert the character, the key is assigned to, here `%'. +With universal arg \C-u insert a `%'. " + (interactive "P") + (let ((parse-sexp-ignore-comments t)) + (if arg + (self-insert-command (if (numberp arg) arg 1)) + (cond + ((and (not match-paren-no-use-syntax-pps) (looking-at "\\s(")) + (forward-list 1) + (backward-char 1)) + ((and (not match-paren-no-use-syntax-pps)(looking-at "\\s)")) + (forward-char 1) (backward-list 1)) + ;; if match-paren-no-syntax-pps + ((looking-at "(") + (ar-parentized-end-atpt)) + ((looking-at ")") + (ar-parentized-beginning-atpt)) + ((looking-at "\\\[") + (ar-bracketed-end-atpt)) + ((looking-at "]") + (ar-bracketed-beginning-atpt)) + ((looking-at "{") + (ar-braced-end-atpt)) + ((looking-at "}") + (ar-braced-beginning-atpt)) + (t (self-insert-command 1)))))) + +(defun py-travel-current-indent (indent) + "Moves down until clause is closed, i.e. current indentation is reached. + +Takes a list, INDENT and START position. " + (let (last) + (while (and (setq last (point))(not (eobp))(py-end-of-statement) + (<= indent (progn (save-excursion (py-beginning-of-statement)(current-indentation)))))) + (when last (goto-char last)) + last)) + +;;; python-mode-execute.el +(defun py-toggle-execute-keep-temporary-file-p () + "Toggle py-execute-keep-temporary-file-p " + (interactive) + (setq py-execute-keep-temporary-file-p + (not py-execute-keep-temporary-file-p)) + (when (and py-verbose-p (interactive-p)) (message "py-execute-keep-temporary-file-p: %s" py-execute-keep-temporary-file-p))) + +(defun py-comint-output-filter-function (string) + "Watch output for Python prompt and exec next file waiting in queue. +This function is appropriate for `comint-output-filter-functions'." + ;; TBD: this should probably use split-string + (when (and (or (string-equal string ">>> ") + (and (>= (length string) 5) + (string-equal (substring string -5) "\n>>> "))) + (or (setq py-shell-input-lines nil) + py-file-queue)) + (pop-to-buffer (current-buffer)) + (ignore-errors (delete-file (car py-file-queue))) + (setq py-file-queue (cdr py-file-queue)) + (if py-file-queue + (let ((pyproc (get-buffer-process (current-buffer)))) + (py-execute-file pyproc (car py-file-queue)))))) + +(defun py-guess-default-python () + "Defaults to \"python\", if guessing didn't succeed. " + (interactive) + (let* ((cmd (or py-shell-name (py-choose-shell) "python")) + (erg (if py-edit-only-p cmd (executable-find cmd)))) + (when (interactive-p) + (if erg + (message "%s" cmd) + (message "%s" "Could not detect Python on your system"))))) + +(defun py-separator-char () + "Return the file-path separator char from current machine. + +When `py-separator-char' is customized, its taken. +Returns char found. " + (let ((erg (cond ((characterp py-separator-char) + (char-to-string py-separator-char)) + ;; epd hack + ((and + (string-match "[Ii][Pp]ython" py-shell-name) + (string-match "epd\\|EPD" py-shell-name)) + (setq erg (shell-command-to-string (concat py-shell-name " -c \"import os; print(os.sep)\""))) + (setq erg (replace-regexp-in-string "\n" "" erg)) + (when (string-match "^$" erg) + (setq erg (substring erg (string-match "^$" erg))))) + (t (setq erg (shell-command-to-string (concat py-shell-name " -W ignore" " -c \"import os; print(os.sep)\""))))))) + (replace-regexp-in-string "\n" "" erg))) + +(unless py-separator-char (setq py-separator-char (py-separator-char))) + +(defun py-process-name (&optional name dedicated nostars sepchar) + "Return the name of the running Python process, `get-process' willsee it. " + (let* ((sepchar (or sepchar (char-to-string py-separator-char))) + (thisname (if name + (if (string-match sepchar name) + (substring name (progn (string-match (concat "\\(.+\\)" sepchar "\\(.+\\)$") name) (match-beginning 2))) + + name) + (substring py-shell-name (or (string-match (concat sepchar ".+$") py-shell-name) 0)))) + (nname (cond (dedicated + (make-temp-name (concat thisname "-"))) + ;; ((string-match "\*" (buffer-name)) + ;; (replace-regexp-in-string "\*" "" (buffer-name))) + (t thisname))) + (erg (cond ((or (string-match "ipython" nname) + (string-match "IPython" nname)) + "IPython") + (nname)))) + (unless (or nostars (string-match "^\*" erg))(setq erg (concat "*" erg "*"))) + erg)) + + +(defvar ipython-completion-command-string nil + "Either ipython0.10-completion-command-string or ipython0.11-completion-command-string. + +ipython0.11-completion-command-string also covers version 0.12") +;; (make-variable-buffer-local 'ipython-completion-command-string) + +(defvar ipython0.10-completion-command-string + "print(';'.join(__IP.Completer.all_completions('%s'))) #PYTHON-MODE SILENT\n" + "The string send to ipython to query for all possible completions") + +;; (setq ipython0.10-completion-command-string "print(';'.join(__IP.Completer.all_completions('%s'))) #PYTHON-MODE SILENT\n") + +(defvar ipython0.11-completion-command-string + "print(';'.join(get_ipython().Completer.all_completions('%s'))) #PYTHON-MODE SILENT\n" + "The string send to ipython to query for all possible completions") + +;; from ipython.el +(defun py-dirstack-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)) + +(defun py-set-ipython-completion-command-string (&optional pyshellname) + "Set and return `ipython-completion-command-string'. " + (interactive) + (let* ((pyshellname (or pyshellname py-shell-name)) + (ipython-version + (when (string-match "ipython" pyshellname) + (string-to-number (substring (shell-command-to-string (concat pyshellname " -V")) 2 -1))))) + (when ipython-version + (setq ipython-completion-command-string (if (< ipython-version 11) ipython0.10-completion-command-string ipython0.11-completion-command-string)) + ipython-completion-command-string))) + +(defalias 'py-dedicated-shell 'py-shell-dedicated) +(defun py-shell-dedicated (&optional argprompt) + "Start an interactive Python interpreter in another window. + +With optional \\[universal-argument] user is prompted by +`py-choose-shell' for command and options to pass to the Python +interpreter. +" + (interactive "P") + (py-shell argprompt t)) + +(defun py-buffer-name-prepare (name &optional sepchar dedicated) + "Return an appropriate name to display in modeline. +SEPCHAR is the file-path separator of your system. " + (let ((sepchar (or sepchar (char-to-string py-separator-char))) + prefix erg suffix) + (when (string-match (regexp-quote sepchar) name) + (unless py-modeline-acronym-display-home-p + (when (string-match (concat "^" (expand-file-name "~")) name) + (setq name (replace-regexp-in-string (concat "^" (expand-file-name "~")) "" name)))) + (save-match-data + (setq liste (split-string name sepchar))) + (dolist (ele liste) + (unless (string= "" ele) + (setq prefix (concat prefix (char-to-string (aref ele 0)))))) + (unless py-modeline-display-full-path-p + + (setq name (substring name (1+ (string-match (concat sepchar "[^" sepchar "]+$") name)))))) + (setq erg + (cond ((string= "ipython" name) + (replace-regexp-in-string "ipython" "IPython" name)) + ((string= "jython" name) + (replace-regexp-in-string "jython" "Jython" name)) + ((string= "python" name) + (replace-regexp-in-string "python" "Python" name)) + ((string-match "python2" name) + (replace-regexp-in-string "python2" "Python2" name)) + ((string-match "python3" name) + (replace-regexp-in-string "python3" "Python3" name)) + (t name))) + (when dedicated + (setq erg (make-temp-name (concat erg "-")))) + (cond ((and prefix (string-match "^\*" erg)) + (setq erg (replace-regexp-in-string "^\*" (concat "*" prefix " ") erg))) + (prefix + (setq erg (concat "*" prefix " " erg "*"))) + + (t (setq erg (concat "*" erg "*")))) + erg)) + +(defun py-delete-numbers-and-stars-from-string (string) + "Delete numbering and star chars from string, return result. + +Needed when file-path names are contructed from maybe numbered buffer names like \"\*Python\*<2> \"" + (replace-regexp-in-string + "<\\([0-9]+\\)>" "" + (replace-regexp-in-string + "\*" "" + string))) + +(defun py-shell-manage-windows (switch py-split-windows-on-execute-p py-switch-buffers-on-execute-p oldbuf py-buffer-name) + (delete-other-windows) + (window-configuration-to-register 213465889) + (cond (;; split and switch + (unless (eq switch 'noswitch) + (and py-split-windows-on-execute-p + (or (eq switch 'switch) + py-switch-buffers-on-execute-p))) + (unless (string-match "[Ii][Pp]ython" py-buffer-name) (delete-other-windows)) + (when (< (count-windows) 2) + (funcall py-split-windows-on-execute-function)) + (pop-to-buffer py-buffer-name)) + ;; split, not switch + ((and py-split-windows-on-execute-p + (or (eq switch 'noswitch) + (not (eq switch 'switch)))) + (if (< (count-windows) 2) + (progn + (funcall py-split-windows-on-execute-function) + (display-buffer py-buffer-name) + ;; avoids windows flip top-down - by side-effect? + (window-configuration-to-register 213465889)) + (window-configuration-to-register 213465889)) + (jump-to-register 213465889) + (display-buffer oldbuf) + (pop-to-buffer oldbuf)) + ;; no split, switch + ((or (eq switch 'switch) + (and (not (eq switch 'noswitch)) + py-switch-buffers-on-execute-p)) + (pop-to-buffer py-buffer-name) + (goto-char (point-max))) + ;; no split, no switch + ((or (eq switch 'noswitch) + (not py-switch-buffers-on-execute-p)) + (set-buffer oldbuf) + (switch-to-buffer (current-buffer))))) + +(defun py-report-executable (py-buffer-name) + (let ((erg (downcase (replace-regexp-in-string + "<\\([0-9]+\\)>" "" + (replace-regexp-in-string + "\*" "" + (if + (string-match " " py-buffer-name) + (substring py-buffer-name (1+ (string-match " " py-buffer-name))) + py-buffer-name)))))) + (when (string-match "-" erg) + (setq erg (substring erg 0 (string-match "-" erg)))) + erg)) + +(defun py-shell-send-setup-code (process) + "Send all setup code for shell. +This function takes the list of setup code to send from the +`python-shell-setup-codes' list." + (accept-process-output process 1) + (dolist (code python-shell-setup-codes) + (python-shell-send-string-no-output + (symbol-value code) process) + (sit-for 0.1))) + +(defun py-shell (&optional argprompt dedicated pyshellname switch sepchar py-buffer-name done) + "Start an interactive Python interpreter in another window. + +Interactively, \\[universal-argument] 4 prompts for a buffer. +\\[universal-argument] 2 prompts for `py-python-command-args'. +If `default-directory' is a remote file name, it is also prompted +to change if called with a prefix arg. + +Returns py-shell's buffer-name. +Optional string PYSHELLNAME overrides default `py-shell-name'. +Optional symbol SWITCH ('switch/'noswitch) precedes `py-switch-buffers-on-execute-p' +When SEPCHAR is given, `py-shell' must not detect the file-separator. +BUFFER allows specifying a name, the Python process is connected to +When DONE is `t', `py-shell-manage-windows' is omitted +" + (interactive "P") + (if (or argprompt dedicated pyshellname switch sepchar py-buffer-name done (interactive-p)) + (let* ((sepchar (or sepchar (char-to-string py-separator-char))) + (args py-python-command-args) + (oldbuf (current-buffer)) + (path (getenv "PYTHONPATH")) + ;; make classic python.el forms usable, to import emacs.py + (process-environment + (cons (concat "PYTHONPATH=" + (if path (concat path path-separator)) + data-directory) + process-environment)) + ;; proc + (py-buffer-name + (or py-buffer-name + (when argprompt + (cond + ((eq 4 (prefix-numeric-value argprompt)) + (setq py-buffer-name + (prog1 + (read-buffer "Py-Shell buffer: " + (generate-new-buffer-name (py-buffer-name-prepare (or pyshellname py-shell-name) sepchar))) + (if (file-remote-p default-directory) + ;; It must be possible to declare a local default-directory. + (setq default-directory + (expand-file-name + (read-file-name + "Default directory: " default-directory default-directory + t nil 'file-directory-p))))))) + ((and (eq 2 (prefix-numeric-value argprompt)) + (fboundp 'split-string)) + (setq args (split-string + (read-string "Py-Shell arguments: " + (concat + (mapconcat 'identity py-python-command-args " ") " "))))))))) + (pyshellname (or pyshellname + (if (or (null py-shell-name)(string= "" py-shell-name)) + (py-choose-shell) + py-shell-name))) + ;; If we use a pipe, Unicode characters are not printed + ;; correctly (Bug#5794) and IPython does not work at + ;; all (Bug#5390). python.el + (process-connection-type t) + ;; already in py-choose-shell + (py-use-local-default + (if (not (string= "" py-shell-local-path)) + (expand-file-name py-shell-local-path) + (when py-use-local-default + (error "Abort: `py-use-local-default' is set to `t' but `py-shell-local-path' is empty. Maybe call `py-toggle-local-default-use'")))) + (py-buffer-name-prepare (unless py-buffer-name + (py-buffer-name-prepare (or pyshellname py-shell-name) sepchar dedicated))) + (py-buffer-name (or py-buffer-name py-buffer-name-prepare)) + (executable (cond (pyshellname) + (py-buffer-name + (py-report-executable py-buffer-name)))) + proc) + (set-buffer (apply 'make-comint-in-buffer executable py-buffer-name executable nil args)) + (set (make-local-variable 'comint-prompt-regexp) + (concat "\\(" + (mapconcat 'identity + (delq nil (list py-shell-input-prompt-1-regexp py-shell-input-prompt-2-regexp ipython-de-input-prompt-regexp ipython-de-output-prompt-regexp py-pdbtrack-input-prompt py-pydbtrack-input-prompt)) + "\\|") + "\\)")) + (set (make-local-variable 'comint-input-filter) 'py-history-input-filter) + + (set (make-local-variable 'comint-use-prompt-regexp) t) + (set (make-local-variable 'compilation-error-regexp-alist) + python-compilation-regexp-alist) + (setq completion-at-point-functions nil) + ;; (py-set-shell-complete-function) + ;; (if py-complete-function + ;; (add-hook 'completion-at-point-functions + ;; py-complete-function nil 'local) + ;; (add-hook 'completion-at-point-functions + ;; 'py-shell-complete nil 'local)) + (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter + nil t) + (if py-fontify-shell-buffer-p + (progn + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil + (font-lock-syntactic-keywords + . python-font-lock-syntactic-keywords))) + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-start-skip) "^[ \t]*#+ *") + (set (make-local-variable 'comment-column) 40) + (set (make-local-variable 'comment-indent-function) #'py-comment-indent-function) + (set (make-local-variable 'indent-region-function) 'py-indent-region) + (set (make-local-variable 'indent-line-function) 'py-indent-line) + (font-lock-fontify-buffer)) + (font-lock-unfontify-region (point-min) (line-beginning-position))) + (setq python-buffer (current-buffer)) + ;; (accept-process-output (get-buffer-process python-buffer) 5) + (setq proc (get-buffer-process (current-buffer))) + (goto-char (point-max)) + (move-marker (process-mark proc) (point-max)) + ;; (funcall (process-filter proc) proc "") + (py-shell-send-setup-code proc) + ;; (accept-process-output proc 1) + (compilation-shell-minor-mode 1) + ;; (sit-for 0.1) + (setq comint-input-sender 'py-shell-simple-send) + (setq comint-input-ring-file-name + (cond ((string-match "[iI][pP]ython[[:alnum:]]*$" py-buffer-name) + (if (getenv "IPYTHONDIR") + (concat (getenv "IPYTHONDIR") "/history") + "~/.ipython/history")) + ((getenv "PYTHONHISTORY") + (concat (getenv "PYTHONHISTORY") "/" (py-report-executable py-buffer-name) "_history")) + (dedicated + (concat "~/." (substring py-buffer-name 0 (string-match "-" py-buffer-name)) "_history")) + ;; .pyhistory might be locked from outside Emacs + ;; (t "~/.pyhistory") + (t (concat "~/." (py-report-executable py-buffer-name) "_history")))) + (comint-read-input-ring t) + (set-process-sentinel (get-buffer-process (current-buffer)) + #'shell-write-history-on-exit) + ;; pdbtrack + ;; (add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file) + (setq py-pdbtrack-do-tracking-p t) + ;; + (set-syntax-table python-mode-syntax-table) + (ansi-color-for-comint-mode-on) + (use-local-map py-shell-map) + ;; (add-hook 'py-shell-hook 'py-dirstack-hook) + (when py-shell-hook (run-hooks 'py-shell-hook)) + (if (and (interactive-p) py-shell-switch-buffers-on-execute-p) + (pop-to-buffer py-buffer-name) + (unless done (py-shell-manage-windows switch py-split-windows-on-execute-p py-switch-buffers-on-execute-p oldbuf py-buffer-name))) + py-buffer-name) + (cond ((comint-check-proc (current-buffer)) + (buffer-name (current-buffer))) + ((comint-check-proc py-buffer-name) + py-buffer-name)))) + +(defalias 'iyp 'ipython) +(defalias 'ipy 'ipython) +;;; Named shells +(defun python (&optional argprompt dedicated switch) + "Start an Python interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Python interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "python" switch)) + +(defalias 'ipyhton 'ipython) +(defalias 'iypthon 'ipython) +(defun ipython (&optional argprompt dedicated switch) + "Start an IPython interpreter. + +Optional \\[universal-argument] prompts for options to pass to the IPython interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "ipython" switch)) + +(defun python3 (&optional argprompt dedicated switch) + "Start an Python3 interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Python3 interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "python3" switch)) + +(defun python2 (&optional argprompt dedicated switch) + "Start an Python2 interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Python2 interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "python2" switch)) + +(defun python2.7 (&optional argprompt dedicated switch) + "Start an Python2.7 interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "python2.7" switch)) + +(defun jython (&optional argprompt dedicated switch) + "Start an Jython interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "jython" switch)) + +(defun bpython (&optional argprompt dedicated switch) + "Start an BPython interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "bpython" switch)) + +(defun python3.2 (&optional argprompt dedicated switch) + "Start an Python3.2 interpreter. + +Optional \\[universal-argument] prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'. + Optional DEDICATED SWITCH are provided for use from programs. " + (interactive "P") + (py-shell argprompt dedicated "python3.2" switch)) + +;; dedicated +(defun python-dedicated (&optional argprompt switch) + "Start an unique Python interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python" switch)) + +(defun ipython-dedicated (&optional argprompt switch) + "Start an unique IPython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the IPython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "ipython" switch)) + +(defun python3-dedicated (&optional argprompt switch) + "Start an unique Python3 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python3" switch)) + +(defun python2-dedicated (&optional argprompt switch) + "Start an unique Python2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python2" switch)) + +(defun python2.7-dedicated (&optional argprompt switch) + "Start an unique Python2.7 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python2.7" switch)) + +(defun jython-dedicated (&optional argprompt switch) + "Start an unique Jython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "jython" switch)) + +(defun python3.2-dedicated (&optional argprompt switch) + "Start an unique Python3.2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python3.2" switch)) + +;; switch +(defun python-switch (&optional argprompt dedicated) + "Switch to Python interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python" 'switch)) + +(defun ipython-switch (&optional argprompt dedicated) + "Switch to IPython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the IPython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "ipython" 'switch)) + +(defun python3-switch (&optional argprompt dedicated) + "Switch to Python3 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python3" 'switch)) + +(defun python2-switch (&optional argprompt dedicated) + "Switch to Python2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python2" 'switch)) + +(defun python2.7-switch (&optional argprompt dedicated) + "Switch to Python2.7 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python2.7" 'switch)) + +(defun jython-switch (&optional argprompt dedicated) + "Switch to Jython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "jython" 'switch)) + +(defun python3.2-switch (&optional argprompt dedicated) + "Switch to Python3.2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python3.2" 'switch)) + +(defun python-no-switch (&optional argprompt dedicated) + "Open an Python interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Python interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python" 'noswitch)) + +(defun ipython-no-switch (&optional argprompt dedicated) + "Open an IPython interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the IPython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "ipython" 'noswitch)) + +(defun python3-no-switch (&optional argprompt dedicated) + "Open an Python3 interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Python3 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python3" 'noswitch)) + +(defun python2-no-switch (&optional argprompt dedicated) + "Open an Python2 interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Python2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python2" 'noswitch)) + +(defun python2.7-no-switch (&optional argprompt dedicated) + "Open an Python2.7 interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python2.7" 'noswitch)) + +(defun jython-no-switch (&optional argprompt dedicated) + "Open an Jython interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "jython" 'noswitch)) + +(defun python3.2-no-switch (&optional argprompt dedicated) + "Open an Python3.2 interpreter in another window, but do not switch to it. + +Optional \\[universal-argument] prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt dedicated "python3.2" 'noswitch)) + +(defalias 'python-dedicated-switch 'python-switch-dedicated) +(defun python-switch-dedicated (&optional argprompt) + "Switch to an unique Python interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python" 'switch)) + +(defalias 'ipython-dedicated-switch 'ipython-switch-dedicated) +(defun ipython-switch-dedicated (&optional argprompt) + "Switch to an unique IPython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the IPython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "ipython" 'switch)) + +(defalias 'python3-dedicated-switch 'python3-switch-dedicated) +(defun python3-switch-dedicated (&optional argprompt) + "Switch to an unique Python3 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python3" 'switch)) + +(defalias 'python2-dedicated-switch 'python2-switch-dedicated) +(defun python2-switch-dedicated (&optional argprompt) + "Switch to an unique Python2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python2" 'switch)) + +(defalias 'python2.7-dedicated-switch 'python2.7-switch-dedicated) +(defun python2.7-switch-dedicated (&optional argprompt) + "Switch to an unique Python2.7 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python2.7" 'switch)) + +(defalias 'jython-dedicated-switch 'jython-switch-dedicated) +(defun jython-switch-dedicated (&optional argprompt) + "Switch to an unique Jython interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Jython interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "jython" 'switch)) + +(defalias 'python3.2-dedicated-switch 'python3.2-switch-dedicated) +(defun python3.2-switch-dedicated (&optional argprompt) + "Switch to an unique Python3.2 interpreter in another window. + +Optional \\[universal-argument] prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'." + (interactive "P") + (py-shell argprompt t "python3.2" 'switch)) + + +;;; Code execution commands +(declare-function compilation-shell-minor-mode "compile" (&optional arg)) + +(defun py-which-execute-file-command (filename) + "Return the command appropriate to Python version. + +Per default it's \"(format \"execfile(r'%s') # PYTHON-MODE\\n\" filename)\" for Python 2 series." + (interactive) + (let* ((erg (py-which-python)) + (cmd (if (< erg 3) + (format "execfile(r'%s') # PYTHON-MODE\n" filename) + (format "exec(compile(open('%s').read(), '%s', 'exec')) # PYTHON-MODE\n" filename filename)))) + (when (and py-verbose-p (interactive-p)) (message "%s" (prin1-to-string cmd))) + cmd)) + +(defun py-execute-region-no-switch (start end &optional shell dedicated) + "Send the region to a Python interpreter. + +Ignores setting of `py-switch-buffers-on-execute-p', buffer with region stays current. + " + (interactive "r\nP") + (py-execute-base start end py-shell-name dedicated 'noswitch)) + +(defun py-execute-region-switch (start end &optional shell dedicated) + "Send the region to a Python interpreter. + +Ignores setting of `py-switch-buffers-on-execute-p', output-buffer will being switched to. +" + (interactive "r\nP") + (py-execute-base start end py-shell-name dedicated 'switch)) + +(defun py-execute-region (start end &optional shell dedicated switch) + "Send the region to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch) +" + (interactive "r\nP") + (let ((shell (cond ((and py-force-local-shell-p py-force-py-shell-name-p) + py-shell-name) + ((or py-force-py-shell-name-p (eq 4 (prefix-numeric-value shell))) (default-value 'py-shell-name)) + ((and (numberp shell) (not (eq 1 (prefix-numeric-value shell)))) + (read-from-minibuffer "(path-to-)shell-name: " (default-value 'py-shell-name))) + (t shell)))) + (py-execute-base start end shell dedicated switch))) + +(defun py-execute-region-default (start end &optional dedicated) + "Send the region to the systems default Python interpreter. +See also `py-execute-region'. " + (interactive "r\nP") + (py-execute-base start end (default-value 'py-shell-name) dedicated)) + +(defun py-execute-region-dedicated (start end &optional shell) + "Get the region processed by an unique Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. " + (interactive "r\nP") + (let ((shell (cond ((eq 4 (prefix-numeric-value shell)) (default-value 'py-shell-name)) + ((and (numberp shell) (not (eq 1 (prefix-numeric-value shell)))) + (read-from-minibuffer "(path-to-)shell-name: " (default-value 'py-shell-name))) + (t shell)))) + (py-execute-base start end shell t))) + +(defalias 'py-execute-region-dedicated-default 'py-execute-region-default-dedicated) +(defun py-execute-region-default-dedicated (start end) + "Send the region to an unique shell of systems default Python. " + (interactive "r") + (py-execute-base start end (default-value 'py-shell-name) t)) + +(defun py-execute-base (start end &optional pyshellname dedicated switch nostars sepchar) + "Adapt the variables used in the process. " + (let* ((oldbuf (current-buffer)) + (pyshellname (or pyshellname (py-choose-shell))) + (py-execute-directory (or (ignore-errors (file-name-directory (buffer-file-name)))(getenv "WORKON_HOME")(getenv "HOME"))) + (strg (buffer-substring-no-properties start end)) + (sepchar (or sepchar (char-to-string py-separator-char))) + (py-buffer-name (py-buffer-name-prepare pyshellname sepchar)) + (temp (make-temp-name + (concat (replace-regexp-in-string (regexp-quote sepchar) "-" (replace-regexp-in-string (concat "^" (regexp-quote sepchar)) "" (replace-regexp-in-string ":" "-" pyshellname))) "-"))) + (file (concat (expand-file-name py-temp-directory) sepchar (replace-regexp-in-string (regexp-quote sepchar) "-" temp) ".py")) + (filebuf (get-buffer-create file)) + (proc (if dedicated + (get-buffer-process (py-shell nil dedicated pyshellname switch sepchar py-buffer-name t)) + (or (get-buffer-process (py-buffer-name-prepare pyshellname)) + (get-buffer-process (py-shell nil dedicated pyshellname switch sepchar py-buffer-name t))))) + (procbuf (process-buffer proc)) + (pec (if (string-match "[pP]ython ?3" py-buffer-name) + (format "exec(compile(open('%s').read(), '%s', 'exec')) # PYTHON-MODE\n" file file) + (format "execfile(r'%s') # PYTHON-MODE\n" file))) + (wholebuf (when (boundp 'wholebuf) wholebuf)) + (comint-scroll-to-bottom-on-output t) + erg) + (set-buffer filebuf) + (erase-buffer) + (insert strg) + (py-fix-start (point-min)(point-max)) + (py-if-needed-insert-shell (prin1-to-string proc) sepchar) + (unless wholebuf (py-insert-coding)) + (unless (string-match "[jJ]ython" pyshellname) (py-insert-execute-directory)) + (cond (python-mode-v5-behavior-p + + (let ((cmd (concat pyshellname (if (string-equal py-which-bufname + "Jython") + " -" " -c ")))) + (save-excursion + (set-buffer filebuf) + (shell-command-on-region (point-min) (point-max) + cmd py-output-buffer)) + (if (not (get-buffer py-output-buffer)) + (message "No output.") + (setq py-exception-buffer (current-buffer)) + (let ((err-p (py-postprocess-output-buffer py-output-buffer))) + ;; (when py-switch-buffers-on-execute-p + (pop-to-buffer py-output-buffer) + ;; ) + (if err-p + (pop-to-buffer py-exception-buffer)))))) + (t (set-buffer filebuf) + (write-region (point-min) (point-max) file nil t nil 'ask) + (set-buffer-modified-p 'nil) + (kill-buffer filebuf) + (if (file-readable-p file) + (progn + (when (string-match "ipython" (process-name proc)) + (sit-for py-ipython-execute-delay)) + (setq erg (py-execute-file-base proc file pec procbuf)) + (setq py-exception-buffer (cons file (current-buffer))) + (py-shell-manage-windows switch py-split-windows-on-execute-p py-switch-buffers-on-execute-p oldbuf py-buffer-name) + (unless (string= (buffer-name (current-buffer)) (buffer-name procbuf)) + (when py-verbose-p (message "Output buffer: %s" procbuf))) + (sit-for 0.1) + (unless py-execute-keep-temporary-file-p + (delete-file file) + (when (buffer-live-p file) + (kill-buffer file))) + erg) + (message "%s not readable. %s" file "Do you have write permissions?")))))) + +(defun py-execute-string (&optional string shell dedicated) + "Send the argument STRING to a Python interpreter. + +See also `py-execute-region'. " + (interactive) + (let ((string (or string (read-from-minibuffer "String: "))) + (shell (or shell (default-value 'py-shell-name)))) + (with-temp-buffer + (insert string) + (py-execute-region (point-min) (point-max) shell dedicated)))) + +(defun py-execute-string-dedicated (&optional string shell) + "Send the argument STRING to an unique Python interpreter. + +See also `py-execute-region'. " + (interactive) + (let ((string (or string (read-from-minibuffer "String: "))) + (shell (or shell (default-value 'py-shell-name)))) + (with-temp-buffer + (insert string) + (py-execute-region (point-min) (point-max) shell t)))) + +(defun py-if-needed-insert-shell (&optional name sepchar) + (let ((erg (or name + (py-choose-shell-by-shebang) + (py-choose-shell-by-import) + py-shell-name)) + (sepchar (or sepchar (char-to-string py-separator-char)))) + (when (string-match " " erg) (setq erg (substring erg (1+ (string-match " " erg)))) + ;; closing ">" + (setq erg (substring erg 0 (1- (length erg))))) + (goto-char (point-min)) + (while (empty-line-p) (delete-region (point) (1+ (line-end-position)))) + (unless (looking-at py-shebang-regexp) + (if (string-match (concat "^" erg) "ipython") + (progn + (shell-command "type ipython" t) + (switch-to-buffer (current-buffer)) + (when (looking-at "[^/\n\r]+") + (replace-match "#! "))) + (if (string-match (regexp-quote sepchar) erg) + (insert (concat "#! " erg "\n\n")) + (insert (concat py-shebang-startstring " " erg "\n\n"))))))) + +(defun py-insert-execute-directory () + (goto-char (point-min)) + (if (re-search-forward py-encoding-string-re nil (quote move)) + (progn + (newline) + (insert (concat "import os; os.chdir(\"" py-execute-directory "\")\n"))) + (goto-char (point-min)) + (forward-line 2) + (newline) + (insert (concat "import os; os.chdir(\"" py-execute-directory "\")\n")))) + +(defun py-insert-coding () + ;; (switch-to-buffer (current-buffer)) + (goto-char (point-min)) + (unless (re-search-forward py-encoding-string-re nil t) + (goto-char (point-min)) + (if (re-search-forward py-shebang-regexp nil t 1) + (progn + (newline) + (insert (concat py-encoding-string "\n"))) + (insert (concat py-encoding-string "\n"))))) + +(defun py-if-needed-insert-if () + "Internal use by py-execute... functions. +Inserts an incentive true form \"if 1:\\n.\" " + (let ((needs-if (/= (py-point 'bol) (py-point 'boi)))) + (when needs-if + (insert "if 1:\n") + (setq py-line-number-offset (- py-line-number-offset 1))))) + +(defun py-fix-start (start end) + "Internal use by py-execute... functions. +Avoid empty lines at the beginning. " + (goto-char start) + (let ((beg (copy-marker start))) + (while (empty-line-p) + (delete-region (line-beginning-position) (1+ (line-end-position)))) + (back-to-indentation) + (unless (eq (current-indentation) 0) + (py-shift-left (current-indentation) start end)) + (setq py-line-number-offset (count-lines 1 start)) + beg)) + +(defun py-fetch-py-master-file () + "Lookup if a `py-master-file' is specified. + +See also doku of variable `py-master-file' " + (interactive) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (when (re-search-forward "^ *# Local Variables:" nil (quote move) 1) + (when + (re-search-forward (concat "^\\( *# py-master-file: *\\)\"\\([^ \t]+\\)\" *$") nil t 1) + (setq py-master-file (match-string-no-properties 2)))))) + (when (and py-verbose-p (interactive-p)) (message "%s" py-master-file))) + +(defun py-execute-import-or-reload (&optional argprompt shell dedicated) + "Import the current buffer's file in a Python interpreter. + +If the file has already been imported, then do reload instead to get +the latest version. + +If the file's name does not end in \".py\", then do execfile instead. + +If the current buffer is not visiting a file, do `py-execute-buffer' +instead. + +If the file local variable `py-master-file' is non-nil, import or +reload the named file instead of the buffer's file. The file may be +saved based on the value of `py-execute-import-or-reload-save-p'. + +See also `\\[py-execute-region]'. + +This may be preferable to `\\[py-execute-buffer]' because: + + - Definitions stay in their module rather than appearing at top + level, where they would clutter the global namespace and not affect + uses of qualified names (MODULE.NAME). + + - The Python debugger gets line number information about the functions." + (interactive "P") + ;; Check file local variable py-master-file + (if py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (let ((shell (or shell (py-choose-shell argprompt shell dedicated))) + (file (buffer-file-name (current-buffer)))) + (if file + (let ((proc (or + (ignore-errors (get-process (file-name-directory shell))) + (get-buffer-process (py-shell argprompt dedicated (or shell (default-value 'py-shell-name))))))) + ;; Maybe save some buffers + (save-some-buffers (not py-ask-about-save) nil) + (py-execute-file-base proc file + (if (string-match "\\.py$" file) + (let ((m (py-qualified-module-name (expand-file-name file)))) + (if (string-match "python2" (file-name-nondirectory shell)) + (format "import sys\nif sys.modules.has_key('%s'):\n reload(%s)\nelse:\n import %s\n" m m m) + (format "import sys,imp\nif'%s' in sys.modules:\n imp.reload(%s)\nelse:\n import %s\n" m m m))) + ;; (format "execfile(r'%s')\n" file) + (py-which-execute-file-command file)))) + (py-execute-buffer py-shell-name)))) + +(defun py-qualified-module-name (file) + "Find the qualified module name for filename FILE. + +Basically, this goes down the directory tree as long as there are __init__.py files there." + (let ((rec #'(lambda (d f) + (let* ((dir (file-name-directory d)) + (initpy (concat dir "__init__.py"))) + (if (file-exists-p initpy) + (let ((d2 (directory-file-name d))) + (funcall rec (file-name-directory d2) + (concat (file-name-nondirectory d2) "." f))) + f))))) + (funcall rec (file-name-directory file) + (file-name-sans-extension (file-name-nondirectory file))))) + +(defun py-execute-buffer-dedicated (&optional shell) + "Send the contents of the buffer to a unique Python interpreter. + +If the file local variable `py-master-file' is non-nil, execute the +named file instead of the buffer's file. + +If a clipping restriction is in effect, only the accessible portion of the buffer is sent. A trailing newline will be supplied if needed. + +With \\[univeral-argument] user is prompted to specify another then default shell. +See also `\\[py-execute-region]'. " + (interactive "P") + (py-execute-buffer-base shell t)) + +(defun py-execute-buffer-switch (&optional shell dedicated) + "Send the contents of the buffer to a Python interpreter and switches to output. + +If the file local variable `py-master-file' is non-nil, execute the +named file instead of the buffer's file. +If there is a *Python* process buffer, it is used. +If a clipping restriction is in effect, only the accessible portion of the buffer is sent. A trailing newline will be supplied if needed. + +With \\[univeral-argument] user is prompted to specify another then default shell. +See also `\\[py-execute-region]'. " + (interactive "P") + (py-execute-buffer-base shell dedicated 'switch)) + +(defalias 'py-execute-buffer-switch-dedicated 'py-execute-buffer-dedicated-switch) +(defun py-execute-buffer-dedicated-switch (&optional shell) + "Send the contents of the buffer to an unique Python interpreter. + +Ignores setting of `py-switch-buffers-on-execute-p'. +If the file local variable `py-master-file' is non-nil, execute the +named file instead of the buffer's file. + +If a clipping restriction is in effect, only the accessible portion of the buffer is sent. A trailing newline will be supplied if needed. + +With \\[univeral-argument] user is prompted to specify another then default shell. +See also `\\[py-execute-region]'. " + (interactive "P") + (py-execute-buffer-base shell t 'switch)) + +(defun py-execute-buffer (&optional shell dedicated switch) + "Send the contents of the buffer to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +If the file local variable `py-master-file' is non-nil, execute the +named file instead of the buffer's file. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch) " + (interactive "P") + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-buffer-base (&optional shell dedicated switch) + "Honor `py-master-file'. " + (save-excursion + (let ((py-master-file (or py-master-file (py-fetch-py-master-file)))) + (if py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (py-execute-region (point-min) (point-max) shell dedicated switch)))) + +(defun py-execute-buffer-no-switch (&optional shell dedicated) + "Send the contents of the buffer to a Python interpreter but don't switch to output. + +If the file local variable `py-master-file' is non-nil, execute the +named file instead of the buffer's file. +If there is a *Python* process buffer, it is used. +If a clipping restriction is in effect, only the accessible portion of the buffer is sent. A trailing newline will be supplied if needed. + +With \\[univeral-argument] user is prompted to specify another then default shell. +See also `\\[py-execute-region]'. " + (interactive "P") + (py-execute-buffer-base shell dedicated 'noswitch)) + +;; Fixme: Try to define the function or class within the relevant +;; module, not just at top level. +(defun py-execute-defun () + "Send the current defun (class or method) to the inferior Python process." + (interactive) + (save-excursion (py-execute-region (progn (beginning-of-defun) (point)) + (progn (end-of-defun) (point))))) + +(defun py-process-file (filename &optional output-buffer error-buffer) + "Process \"python filename\". + +Optional OUTPUT-BUFFER and ERROR-BUFFER might be given. " + (interactive "fDatei:") + (let ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (output-buffer (or output-buffer (make-temp-name "py-process-file-output"))) + (cmd (py-choose-shell))) + (unless (buffer-live-p output-buffer) + (set-buffer (get-buffer-create output-buffer))) + (shell-command (concat cmd " " filename) output-buffer error-buffer) + (when (interactive-p) (switch-to-buffer output-buffer)))) + +;;; +(defun py-exec-execfile-region (start end &optional shell) + "Execute the region in a Python interpreter. " + (interactive "r\nP") + (let ((shell (if (eq 4 (prefix-numeric-value shell)) + (read-from-minibuffer "Shell: " (default-value 'py-shell-name)) + py-shell-name))) + (let ((strg (buffer-substring-no-properties start end))) + (py-exec-execfile-base strg shell (interactive-p))))) + +(defun py-exec-execfile-base (strg shell iact) + (let* ((temp (make-temp-name (concat (buffer-name) "-"))) + (file (concat (expand-file-name temp) py-temp-directory ".py")) + (imports (py-find-imports)) + (shell shell) + cmd header) + (with-temp-buffer + (insert imports) + (insert strg) + ;; (py-if-needed-insert-if) + (or shell (setq shell (py-choose-shell))) + (py-insert-coding) + (py-if-needed-insert-shell shell) + (setq header (buffer-substring-no-properties (point-min) (point))) + (switch-to-buffer (current-buffer)) + (setq cmd (py-which-execute-file-command file)) + (write-file file)) + (py-exec-execfile file cmd header (concat temp "-output")) + (set-buffer (concat temp "-output")) + (when iact (switch-to-buffer (current-buffer))) + (when (file-readable-p file) + (delete-file file)) + (when iact (message "Output goes to buffer: %s" temp)) + (concat temp "-output"))) + +(defun py-exec-execfile (filename cmd header &optional output-buffer error-buffer) + "Process \"python filename\", +Optional OUTPUT-BUFFER and ERROR-BUFFER might be given.') +" + (interactive "fDatei:") + (let* ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (exec-execfile (concat (make-temp-name (concat filename "-exec-execfile.py"))))) + (set-buffer (get-buffer-create exec-execfile)) + (insert header) + (insert cmd) + (write-file exec-execfile) + (if output-buffer + (progn + (set-buffer (get-buffer-create output-buffer)) + (erase-buffer) + (switch-to-buffer (current-buffer)) + (shell-command (concat "python " exec-execfile) output-buffer error-buffer)) + (with-temp-buffer + (shell-command (concat "python " exec-execfile) output-buffer error-buffer))))) + +;;; Execute forms +(defun py-execute-statement (&optional shell dedicated switch) + "Send statement at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (prog1 + (or (py-beginning-of-statement-p) + (py-beginning-of-statement)))) + (end (py-end-of-statement))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-block (&optional shell dedicated switch) + "Send block at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-block-p) + (py-beginning-of-block))) + (end (py-end-of-block))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-block-or-clause (&optional shell dedicated switch) + "Send block-or-clause at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-block-or-clause-p) + (py-beginning-of-block-or-clause))) + (end (py-end-of-block-or-clause))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-def (&optional shell dedicated switch) + "Send def at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-def-p) + (py-beginning-of-def))) + (end (py-end-of-def))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-class (&optional shell dedicated switch) + "Send class at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-class-p) + (py-beginning-of-class))) + (end (py-end-of-class))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-def-or-class (&optional shell dedicated switch) + "Send def-or-class at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-def-or-class-p) + (py-beginning-of-def-or-class))) + (end (py-end-of-def-or-class))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-expression (&optional shell dedicated switch) + "Send expression at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-expression-p) + (py-beginning-of-expression))) + (end (py-end-of-expression))) + (py-execute-region beg end shell dedicated switch)))) + +(defun py-execute-partial-expression (&optional shell dedicated switch) + "Send partial-expression at point to a Python interpreter. + +When called with \\[univeral-argument], execution through `default-value' of `py-shell-name' is forced. +See also `py-force-py-shell-name-p'. + +When called with \\[univeral-argument] followed by a number different from 4 and 1, user is prompted to specify a shell. This might be the name of a system-wide shell or include the path to a virtual environment. + +When called from a programm, it accepts a string specifying a shell which will be forced upon execute as argument. + +Optional arguments DEDICATED (boolean) and SWITCH (symbols 'noswitch/'switch)" + (interactive "P") + (save-excursion + (let ((beg (or (py-beginning-of-partial-expression-p) + (py-beginning-of-partial-expression))) + (end (py-end-of-partial-expression))) + (py-execute-region beg end shell dedicated switch)))) + +;;; +(defun py-execute-line () + "Send current line from beginning of indent to Python interpreter. " + (interactive) + (save-excursion + (let ((beg (progn (back-to-indentation) + (point)))) + (py-execute-region beg (line-end-position))))) + +(defun py-execute-file (&optional filename shell dedicated switch) + "When called interactively, user is prompted for filename. " + (interactive "fFile: ") + (let* ((regbuf (current-buffer)) + (file (or (expand-file-name filename) (when (ignore-errors (file-readable-p (buffer-file-name))) (buffer-file-name)))) + (shell (or shell (progn (with-temp-buffer (insert-file-contents file)(py-choose-shell))))) + (name (py-process-name shell dedicated)) + (proc (get-buffer-process (py-shell nil dedicated (or shell (downcase name))))) + (procbuf (if dedicated + (buffer-name (get-buffer (current-buffer))) + (buffer-name (get-buffer (concat "*" name "*"))))) + (pec (if (string-match "Python3" name) + (format "exec(compile(open('%s').read(), '%s', 'exec')) # PYTHON-MODE\n" file file) + (format "execfile(r'%s') # PYTHON-MODE\n" file))) + (comint-scroll-to-bottom-on-output t)) + (if (file-readable-p file) + (progn + (setq erg (py-execute-file-base proc file pec)) + (setq py-exception-buffer (cons file (current-buffer))) + (if (or (eq switch 'switch) + (and (not (eq switch 'noswitch)) py-switch-buffers-on-execute-p)) + (progn + (pop-to-buffer procbuf) + (goto-char (point-max))) + (when (buffer-live-p regbuf) (pop-to-buffer regbuf)) + (when py-verbose-p (message "Output buffer: %s" procbuf))) + (sit-for 0.1) + erg) + (message "File not readable: %s" "Do you have write permissions?")))) + +(defun py-execute-file-base (proc filename &optional cmd procbuf) + "Send to Python interpreter process PROC, in Python version 2.. \"execfile('FILENAME')\". + +Make that process's buffer visible and force display. Also make +comint believe the user typed this string so that +`kill-output-from-shell' does The Right Thing. +Returns position where output starts. " + (let ((procbuf (or procbuf (process-buffer proc))) + (comint-scroll-to-bottom-on-output t) + (msg (format "## executing %s...\n" filename)) + (cmd (cond (cmd) + (py-exec-command) + (t (py-which-execute-file-command filename)))) + erg) + (when py-verbose-p + (unwind-protect + (save-excursion + (set-buffer procbuf) + (funcall (process-filter proc) proc msg)))) + (set-buffer procbuf) + (process-send-string proc cmd) + (setq erg (goto-char (process-mark proc))) + erg)) + +;;: Subprocess utilities and filters +(defvar py-last-exeption-buffer nil + "Internal use only - when `py-up-exception' is called in +source-buffer, this will deliver the exception-buffer again. ") + +(defun py-postprocess-output-buffer (buf) + "Highlight exceptions found in BUF. +If an exception occurred return t, otherwise return nil. BUF must exist." + (let (line file bol err-p) + (save-excursion + (set-buffer buf) + (goto-char (point-min)) + (while (re-search-forward py-traceback-line-re nil t) + (setq file (match-string 1) + line (string-to-number (match-string 2)) + bol (py-point 'bol)) + (overlay-put (make-overlay (match-beginning 0) (match-end 0)) + 'face 'highlight))) + (when (and py-jump-on-exception line) + (beep) + (py-jump-to-exception file line py-line-number-offset) + (setq err-p t)) + err-p)) + +(defun py-jump-to-exception (file line py-line-number-offset) + "Jump to the Python code in FILE at LINE." + (let ((buffer (cond ((string-equal file "<stdin>") + (if (consp py-exception-buffer) + (cdr py-exception-buffer) + py-exception-buffer)) + ((and (consp py-exception-buffer) + (string-equal file (car py-exception-buffer))) + (cdr py-exception-buffer)) + ((ignore-errors (find-file-noselect file))) + ;; could not figure out what file the exception + ;; is pointing to, so prompt for it + (t (find-file (read-file-name "Exception file: " + nil + file t)))))) + ;; Fiddle about with line number + (setq line (+ py-line-number-offset line)) + + (pop-to-buffer buffer) + ;; Force Python mode + (unless(eq major-mode 'python-mode) + (python-mode)) + (goto-char (point-min)) + (forward-line (1- line)) + (message "Jumping to exception in file %s on line %d" file line))) + +(defun py-down-exception (&optional bottom) + "Go to the next line down in the traceback. + +With \\[univeral-argument] (programmatically, optional argument +BOTTOM), jump to the bottom (innermost) exception in the exception +stack." + (interactive "P") + (py-find-next-exception-prepare 'down (when (eq 4 (prefix-numeric-value bottom)) "BOTTOM"))) + +(defun py-up-exception (&optional top) + "Go to the previous line up in the traceback. + +With \\[universal-argument] (programmatically, optional argument TOP) +jump to the top (outermost) exception in the exception stack." + (interactive "P") + (unless py-last-exeption-buffer (setq py-last-exeption-buffer (current-buffer))) + (py-find-next-exception-prepare 'up (when (eq 4 (prefix-numeric-value top)) "TOP"))) + +(defun py-find-next-exception-prepare (direction start) + "Setup exception regexps depending from kind of Python shell. " + (let* ((name (get-process (substring (buffer-name (current-buffer)) 1 -1))) + (buffer (cond (name (buffer-name (current-buffer))) + ((buffer-live-p (get-buffer py-output-buffer)) + py-output-buffer) + (py-last-exeption-buffer (buffer-name py-last-exeption-buffer)) + (t (error "Don't see exeption buffer"))))) + (when buffer (set-buffer (get-buffer buffer))) + (switch-to-buffer (current-buffer)) + (if (eq direction 'up) + (if (string= start "TOP") + (py-find-next-exception 'bob buffer 're-search-forward "Top") + (py-find-next-exception 'bol buffer 're-search-backward "Top")) + (if (string= start "BOTTOM") + (py-find-next-exception 'eob buffer 're-search-backward "Bottom") + (py-find-next-exception 'eol buffer 're-search-forward "Bottom"))))) + +(defun py-find-next-exception (start buffer searchdir errwhere) + "Find the next Python exception and jump to the code that caused it. +START is the buffer position in BUFFER from which to begin searching +for an exception. SEARCHDIR is a function, either +`re-search-backward' or `re-search-forward' indicating the direction +to search. ERRWHERE is used in an error message if the limit (top or +bottom) of the trackback stack is encountered." + (let ((orig (point)) + (origline (py-count-lines)) + file line pos) + (goto-char (py-point start)) + (if (funcall searchdir py-traceback-line-re nil t) + (if (save-match-data (eq (py-count-lines) origline)) + (progn + (forward-line (if (string= errwhere "Top") -1 1)) + (py-find-next-exception start buffer searchdir errwhere)) + (if (not (save-match-data (string-match "^IPython\\|^In \\[[0-9]+\\]: *\\|^>>>" (match-string-no-properties 0)))) + (progn + (setq py-last-exeption-buffer (current-buffer)) + (if (save-match-data (string-match "File" (match-string-no-properties 0))) + (progn + (setq file (match-string-no-properties 2) + pos (point) + line (string-to-number (match-string-no-properties 3)))) + (save-excursion + ;; file and line-number are in different lines + (setq line (string-to-number (match-string-no-properties 1)) + pos (point) + file (progn + (when (and (re-search-backward "\\(^IPython\\|^In \\[[0-9]+\\]: *\\|^>>>\\|^[^\t >]+\\)>?[ \t]+in[ \t]+\\([^ \t\n]+\\)" nil t 1) + (not (save-match-data (string-match "<\\|^IPython\\|^In \\[[0-9]+\\]: *\\|^>>>" (match-string-no-properties 1))))) + (match-string-no-properties 1)))))) + (if file + (when (string-match ".+\.pyc" file) + (setq file (substring file 0 -1))) + (error "%s of traceback" errwhere)) + (if (and file line) + (if + (and (string= "<stdin>" file) (eq 1 line)) + (error "%s of traceback" errwhere) + (py-jump-to-exception file line py-line-number-offset)) + (error "%s of traceback" errwhere))) + (goto-char orig) + (error "%s of traceback" errwhere)))))) + +;;; python-mode-send.el + +(defun py-output-buffer-filter (&optional beg end) + "Clear output buffer from py-shell-input prompt etc. " + (interactive "*") + (let ((beg (cond (beg) + ((region-active-p) + (region-beginning)) + (t (point-min)))) + (end (cond (end (copy-marker end)) + ((region-active-p) + (copy-marker (region-end))) + (t (copy-marker (point-max)))))) + (goto-char beg) + (while (re-search-forward (concat "\\(" py-shell-input-prompt-1-regexp "\\|" py-shell-input-prompt-2-regexp "\\|" "^In \\[[0-9]+\\]: *" "\\)") nil (quote move) 1) + (replace-match "")) + (goto-char beg))) + +(defun py-send-string (string &optional process) + "Evaluate STRING in inferior Python process." + (interactive "sPython command: ") + (let ((proc (or process (py-shell)))) + (comint-send-string proc string) + (unless (string-match "\n\\'" string) + ;; Make sure the text is properly LF-terminated. + (comint-send-string proc "\n")) + (when (string-match "\n[ \t].*\n?\\'" string) + ;; If the string contains a final indented line, add a second newline so + ;; as to make sure we terminate the multiline instruction. + (comint-send-string proc "\n")))) + +;;; python-components-pdb.el + +;;; Pdbtrack + +(defun py-pdbtrack-overlay-arrow (activation) + "Activate or de arrow at beginning-of-line in current buffer." + ;; This was derived/simplified from edebug-overlay-arrow + (cond (activation + (setq overlay-arrow-position (make-marker)) + (setq overlay-arrow-string "=>") + (set-marker overlay-arrow-position (line-beginning-position) (current-buffer)) + (setq py-pdbtrack-is-tracking-p t)) + (overlay-arrow-position + (setq overlay-arrow-position nil) + (setq py-pdbtrack-is-tracking-p nil)))) + +(defun py-pdbtrack-track-stack-file (text) + "Show the file indicated by the pdb stack entry line, in a separate window. + +Activity is disabled if the buffer-local variable +`py-pdbtrack-do-tracking-p' is nil. + +We depend on the pdb input prompt matching `py-pdbtrack-input-prompt' +at the beginning of the line. + +If the traceback target file path is invalid, we look for the most +recently visited python-mode buffer which either has the name of the +current function \(or class) or which defines the function \(or +class). This is to provide for remote scripts, eg, Zope's 'Script +\(Python)' - put a _copy_ of the script in a buffer named for the +script, and set to python-mode, and pdbtrack will find it.)" + ;; Instead of trying to piece things together from partial text + ;; (which can be almost useless depending on Emacs version), we + ;; monitor to the point where we have the next pdb prompt, and then + ;; check all text from comint-last-input-end to process-mark. + ;; + ;; Also, we're very conservative about clearing the overlay arrow, + ;; to minimize residue. This means, for instance, that executing + ;; other pdb commands wipe out the highlight. You can always do a + ;; 'where' (aka 'w') command to reveal the overlay arrow. + (let* ((origbuf (current-buffer)) + (currproc (get-buffer-process origbuf))) + + (if (not (and currproc py-pdbtrack-do-tracking-p)) + (py-pdbtrack-overlay-arrow nil) + + (let* ((procmark (process-mark currproc)) + (block (buffer-substring (max comint-last-input-end + (- procmark + py-pdbtrack-track-range)) + procmark)) + target target_fname target_lineno target_buffer) + + (if (not (string-match (concat py-pdbtrack-input-prompt "$") block)) + (py-pdbtrack-overlay-arrow nil) + + (setq target (py-pdbtrack-get-source-buffer block)) + + (if (stringp target) + (message "pdbtrack: %s" target) + + (setq target_lineno (car target)) + (setq target_buffer (cadr target)) + (setq target_fname (buffer-file-name target_buffer)) + (switch-to-buffer-other-window target_buffer) + (goto-char (point-min)) + (forward-line (1- target_lineno)) + (message "pdbtrack: line %s, file %s" target_lineno target_fname) + (py-pdbtrack-overlay-arrow t) + (pop-to-buffer origbuf t))))))) + +(defun py-pdbtrack-map-filename (filename) + + (let + ((replacement-val (assoc-default + filename py-pdbtrack-filename-mapping + (lambda (mapkey path) + (string-match + (concat "^" (regexp-quote mapkey)) + path))) + )) + (if (not (eq replacement-val nil)) + (replace-match replacement-val 't 't filename) + filename))) + +(defun py-pdbtrack-get-source-buffer (block) + "Return line number and buffer of code indicated by block's traceback text. + +We look first to visit the file indicated in the trace. + +Failing that, we look for the most recently visited python-mode buffer +with the same name or having the named function. + +If we're unable find the source code we return a string describing the +problem as best as we can determine." + + (if (and (not (string-match py-pdbtrack-stack-entry-regexp block)) + ;; pydb integration still to be done + ;; (not (string-match py-pydbtrack-stack-entry-regexp block)) + ) + "Traceback cue not found" + (let* ((filename (match-string + py-pdbtrack-marker-regexp-file-group block)) + (lineno (string-to-number (match-string + py-pdbtrack-marker-regexp-line-group + block))) + (funcname (match-string py-pdbtrack-marker-regexp-funcname-group + block)) + funcbuffer) + + (cond ((file-exists-p filename) + (list lineno (find-file-noselect filename))) + + ((file-exists-p (py-pdbtrack-map-filename filename)) + (list lineno (find-file-noselect (py-pdbtrack-map-filename filename)))) + + ((setq funcbuffer (py-pdbtrack-grub-for-buffer funcname lineno)) + (if (string-match "/Script (Python)$" filename) + ;; Add in number of lines for leading '##' comments: + (setq lineno + (+ lineno + (save-excursion + (set-buffer funcbuffer) + (count-lines + (point-min) + (max (point-min) + (string-match "^\\([^#]\\|#[^#]\\|#$\\)" + (buffer-substring (point-min) + (point-max))))))))) + (list lineno funcbuffer)) + + ((= (elt filename 0) ?\<) + (format "(Non-file source: '%s')" filename)) + + (t (format "Not found: %s(), %s" funcname filename)))))) + +(defun py-pdbtrack-grub-for-buffer (funcname lineno) + "Find most recent buffer itself named or having function funcname. + +We walk the buffer-list history for python-mode buffers that are +named for funcname or define a function funcname." + (let ((buffers (buffer-list)) + buf + got) + (while (and buffers (not got)) + (setq buf (car buffers) + buffers (cdr buffers)) + (if (and (save-excursion (set-buffer buf) + (string= major-mode "python-mode")) + (or (string-match funcname (buffer-name buf)) + (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+" + funcname "\\s-*(") + (save-excursion + (set-buffer buf) + (buffer-substring (point-min) + (point-max)))))) + (setq got buf))) + got)) + + +;; pdbtrack functions +(defun py-pdbtrack-toggle-stack-tracking (arg) + "Set variable `py-pdbtrack-do-tracking-p'. " + (interactive "P") + (if (not (get-buffer-process (current-buffer))) + (error "No process associated with buffer '%s'" (current-buffer))) + ;; missing or 0 is toggle, >0 turn on, <0 turn off + (if (or (not arg) + (zerop (setq arg (prefix-numeric-value arg)))) + (setq py-pdbtrack-do-tracking-p (not py-pdbtrack-do-tracking-p)) + (setq py-pdbtrack-do-tracking-p (> arg 0))) + (message "%sabled Python's pdbtrack" + (if py-pdbtrack-do-tracking-p "En" "Dis"))) + +(defun turn-on-pdbtrack () + (interactive) + (py-pdbtrack-toggle-stack-tracking 1)) + +(defun turn-off-pdbtrack () + (interactive) + (py-pdbtrack-toggle-stack-tracking 0)) + +;;; python-components-help.el + +(defun py-fetch-docu () + "Lookup in current buffer for the doku for the symbol at point. + +Useful for newly defined symbol, not known to python yet. " + (interactive) + (let* ((symb (prin1-to-string (symbol-at-point))) + (args (py-expression)) + erg) + (save-restriction + (widen) + (goto-char (point-min)) + (when (re-search-forward (concat py-def-or-class-re " *" symb) nil (quote move) 1) + (forward-line 1) + (when (looking-at "[ \t]*\"\"\"\\|[ \t]*'''\\|[ \t]*'[^]+\\|[ \t]*\"[^\"]+") + (goto-char (match-end 0)) + (setq erg (buffer-substring-no-properties (match-beginning 0) (re-search-forward "\"\"\"\\|'''" nil 'move))) + (when erg + (set-buffer (get-buffer-create "*Python-Help*")) + (erase-buffer) + (when (and py-verbose-p (interactive-p)) (switch-to-buffer (current-buffer))) + (insert erg))))))) + +(defun py-find-imports () + "Find top-level imports, updating `python-imports'." + (interactive) + (let* (imports) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward + "^import *[A-Za-z_][A-Za-z_0-9].*\\|^from +[A-Za-z_][A-Za-z_0-9]+ +import .*" nil t) + (setq imports + (concat + imports + (buffer-substring-no-properties (match-beginning 0) (match-end 0)) ";")))) + (when (and py-verbose-p (interactive-p)) (message "%s" imports)) + imports)) + +(defun py-eldoc-function () + "Print help on symbol at point. " + (interactive) + (if (unless (looking-at " ") + (or + + (eq (get-char-property (point) 'face) 'font-lock-keyword-face) + (eq (get-char-property (point) 'face) 'py-builtins-face) + (eq (get-char-property (point) 'face) 'py-exception-name-face) + (eq (get-char-property (point) 'face) 'py-class-name-face) + + )) + + (lexical-let* ((sym (prin1-to-string (symbol-at-point))) + (origfile (buffer-file-name)) + (temp (make-temp-name (buffer-name))) + (file (concat (expand-file-name temp py-temp-directory) ".py")) + (cmd (py-find-imports)) + (no-quotes (save-excursion + (skip-chars-backward "A-Za-z_0-9.") + (and (looking-at "[A-Za-z_0-9.]+") + (string-match "\\." (match-string-no-properties 0)))))) + (setq cmd (concat "import pydoc\n" + cmd)) + (if no-quotes + (setq cmd (concat cmd + "try: pydoc.help(" sym ")\n")) + (setq cmd (concat cmd "try: pydoc.help('" sym "')\n"))) + (setq cmd (concat cmd + "except: + print 'No help available on:', \"" sym "\"")) + (with-temp-buffer + (insert cmd) + (write-file file)) + (py-process-file file "*Python-Help*") + (when (file-readable-p file) + (delete-file file))) + (delete-other-windows))) + +(defalias 'py-help-at-point 'py-describe-symbol) +(defun py-describe-symbol (&optional arg) + "Print help on symbol at point. + +Optional \\[universal-argument] used for debugging, will prevent deletion of temp file. " + (interactive "P") + (let* ((orig (point)) + (beg (progn (when (and (looking-back "(")(not (looking-at "\\sw"))) (forward-char -1)) (skip-chars-backward "a-zA-Z0-9_." (line-beginning-position))(point))) + (end (progn (skip-chars-forward "a-zA-Z0-9_." (line-end-position))(point))) + (sym (buffer-substring-no-properties beg end)) + (origfile (buffer-file-name)) + (temp (make-temp-name (buffer-name))) + (file (concat (expand-file-name temp py-temp-directory) ".py")) + (cmd (py-find-imports))) + (goto-char orig) + (setq cmd (concat "import pydoc\n" + cmd)) + (setq cmd (concat cmd "pydoc.help('" sym "')\n")) + (with-temp-buffer + (insert cmd) + (write-file file)) + (py-process-file file "*Python-Help*") + (when (file-readable-p file) + (unless (eq 4 (prefix-numeric-value arg)) (delete-file file))))) + + +;;; Documentation +(defun py-dump-help-string (str) + (with-output-to-temp-buffer "*Help*" + (let ((locals (buffer-local-variables)) + funckind funcname func funcdoc + (start 0) mstart end + keys) + (while (string-match "^%\\([vc]\\):\\(.+\\)\n" str start) + (setq mstart (match-beginning 0) end (match-end 0) + funckind (substring str (match-beginning 1) (match-end 1)) + funcname (substring str (match-beginning 2) (match-end 2)) + func (intern funcname)) + (princ (substitute-command-keys (substring str start mstart))) + (cond + ((equal funckind "c") ; command + (setq funcdoc (documentation func) + keys (concat + "Key(s): " + (mapconcat 'key-description + (where-is-internal func python-mode-map) + ", ")))) + ((equal funckind "v") ; variable + (setq funcdoc (documentation-property func 'variable-documentation) + keys (if (assq func locals) + (concat + "Local/Global values: " + (prin1-to-string (symbol-value func)) + " / " + (prin1-to-string (default-value func))) + (concat + "Value: " + (prin1-to-string (symbol-value func)))))) + (t ; unexpected + (error "Error in py-dump-help-string, tag `%s'" funckind))) + (princ (format "\n-> %s:\t%s\t%s\n\n" + (if (equal funckind "c") "Command" "Variable") + funcname keys)) + (princ funcdoc) + (terpri) + (setq start end)) + (princ (substitute-command-keys (substring str start)))) + (if (featurep 'xemacs) (print-help-return-message) + (help-print-return-message)))) + +(add-hook 'python-mode-hook + (lambda () + (setq indent-tabs-mode py-indent-tabs-mode) + (set (make-local-variable 'beginning-of-defun-function) 'py-beginning-of-def-or-class) + (set (make-local-variable 'end-of-defun-function) 'py-end-of-def-or-class) + ;; (orgstruct-mode 1) + )) + +(defun py-describe-mode () + "Dump long form of `python-mode' docs." + (interactive) + (py-dump-help-string "Major mode for editing Python files. +Knows about Python indentation, tokens, comments and continuation lines. +Paragraphs are separated by blank lines only. + +Major sections below begin with the string `@'; specific function and +variable docs begin with `->'. + +@EXECUTING PYTHON CODE + +\\[py-execute-import-or-reload]\timports or reloads the file in the Python interpreter +\\[py-execute-buffer]\tsends the entire buffer to the Python interpreter +\\[py-execute-region]\tsends the current region +\\[py-execute-def-or-class]\tsends the current function or class definition +\\[py-execute-string]\tsends an arbitrary string +\\[py-shell]\tstarts a Python interpreter window; this will be used by +\tsubsequent Python execution commands +%c:py-execute-import-or-reload +%c:py-execute-buffer +%c:py-execute-region +%c:py-execute-def-or-class +%c:py-execute-string +%c:py-shell + +@VARIABLES + +py-install-directory\twherefrom `python-mode' looks for extensions +py-indent-offset\tindentation increment +py-block-comment-prefix\tcomment string used by comment-region + +py-shell-name\tshell command to invoke Python interpreter +py-temp-directory\tdirectory used for temp files (if needed) + +py-beep-if-tab-change\tring the bell if tab-width is changed +%v:py-install-directory +%v:py-indent-offset +%v:py-block-comment-prefix +%v:py-shell-name +%v:py-temp-directory +%v:py-beep-if-tab-change + +@KINDS OF LINES + +Each physical line in the file is either a `continuation line' (the +preceding line ends with a backslash that's not part of a comment, or +the paren/bracket/brace nesting level at the start of the line is +non-zero, or both) or an `initial line' (everything else). + +An initial line is in turn a `blank line' (contains nothing except +possibly blanks or tabs), a `comment line' (leftmost non-blank +character is `#'), or a `code line' (everything else). + +Comment Lines + +Although all comment lines are treated alike by Python, Python mode +recognizes two kinds that act differently with respect to indentation. + +An `indenting comment line' is a comment line with a blank, tab or +nothing after the initial `#'. The indentation commands (see below) +treat these exactly as if they were code lines: a line following an +indenting comment line will be indented like the comment line. All +other comment lines (those with a non-whitespace character immediately +following the initial `#') are `non-indenting comment lines', and +their indentation is ignored by the indentation commands. + +Indenting comment lines are by far the usual case, and should be used +whenever possible. Non-indenting comment lines are useful in cases +like these: + +\ta = b # a very wordy single-line comment that ends up being +\t #... continued onto another line + +\tif a == b: +##\t\tprint 'panic!' # old code we've `commented out' +\t\treturn a + +Since the `#...' and `##' comment lines have a non-whitespace +character following the initial `#', Python mode ignores them when +computing the proper indentation for the next line. + +Continuation Lines and Statements + +The `python-mode' commands generally work on statements instead of on +individual lines, where a `statement' is a comment or blank line, or a +code line and all of its following continuation lines (if any) +considered as a single logical unit. The commands in this mode +generally (when it makes sense) automatically move to the start of the +statement containing point, even if point happens to be in the middle +of some continuation line. + +@INDENTATION + +Primarily for entering new code: +\t\\[indent-for-tab-command]\t indent line appropriately +\t\\[py-newline-and-indent]\t insert newline, then indent +\t\\[py-electric-backspace]\t reduce indentation, or delete single character + +Primarily for reindenting existing code: +\t\\[py-guess-indent-offset]\t guess py-indent-offset from file content; change locally +\t\\[universal-argument] \\[py-guess-indent-offset]\t ditto, but change globally + +\t\\[py-indent-region]\t reindent region to match its context +\t\\[py-shift-left]\t shift line or region left by py-indent-offset +\t\\[py-shift-right]\t shift line or region right by py-indent-offset + +Unlike most programming languages, Python uses indentation, and only +indentation, to specify block structure. Hence the indentation supplied +automatically by `python-mode' is just an educated guess: only you know +the block structure you intend, so only you can supply correct +indentation. + +The \\[indent-for-tab-command] and \\[py-newline-and-indent] keys try to suggest plausible indentation, based on +the indentation of preceding statements. E.g., assuming +py-indent-offset is 4, after you enter +\tif a > 0: \\[py-newline-and-indent] +the cursor will be moved to the position of the `_' (_ is not a +character in the file, it's just used here to indicate the location of +the cursor): +\tif a > 0: +\t _ +If you then enter `c = d' \\[py-newline-and-indent], the cursor will move +to +\tif a > 0: +\t c = d +\t _ +`python-mode' cannot know whether that's what you intended, or whether +\tif a > 0: +\t c = d +\t_ +was your intent. In general, `python-mode' either reproduces the +indentation of the (closest code or indenting-comment) preceding +statement, or adds an extra py-indent-offset blanks if the preceding +statement has `:' as its last significant (non-whitespace and non- +comment) character. If the suggested indentation is too much, use +\\[py-electric-backspace] to reduce it. + +Continuation lines are given extra indentation. If you don't like the +suggested indentation, change it to something you do like, and Python- +mode will strive to indent later lines of the statement in the same way. + +If a line is a continuation line by virtue of being in an unclosed +paren/bracket/brace structure (`list', for short), the suggested +indentation depends on whether the current line contains the first item +in the list. If it does, it's indented py-indent-offset columns beyond +the indentation of the line containing the open bracket. If you don't +like that, change it by hand. The remaining items in the list will mimic +whatever indentation you give to the first item. + +If a line is a continuation line because the line preceding it ends with +a backslash, the third and following lines of the statement inherit their +indentation from the line preceding them. The indentation of the second +line in the statement depends on the form of the first (base) line: if +the base line is an assignment statement with anything more interesting +than the backslash following the leftmost assigning `=', the second line +is indented two columns beyond that `='. Else it's indented to two +columns beyond the leftmost solid chunk of non-whitespace characters on +the base line. + +Warning: indent-region should not normally be used! It calls \\[indent-for-tab-command] +repeatedly, and as explained above, \\[indent-for-tab-command] can't guess the block +structure you intend. +%c:indent-for-tab-command +%c:py-newline-and-indent +%c:py-electric-backspace + +The next function may be handy when editing code you didn't write: +%c:py-guess-indent-offset + +The remaining `indent' functions apply to a region of Python code. They +assume the block structure (equals indentation, in Python) of the region +is correct, and alter the indentation in various ways while preserving +the block structure: +%c:py-indent-region +%c:py-shift-left +%c:py-shift-right + +@MARKING & MANIPULATING REGIONS OF CODE + +\\[py-mark-block]\t mark block of lines +\\[py-mark-def-or-class]\t mark smallest enclosing def +\\[universal-argument] \\[py-mark-def-or-class]\t mark smallest enclosing class +\\[comment-region]\t comment out region of code +\\[universal-argument] \\[comment-region]\t uncomment region of code +%c:py-mark-block +%c:py-mark-def-or-class +%c:comment-region + +@MOVING POINT + +\\[py-previous-statement]\t move to statement preceding point +\\[py-next-statement]\t move to statement following point +\\[py-goto-block-up]\t move up to start of current block +\\[py-beginning-of-def-or-class]\t move to start of def +\\[universal-argument] \\[py-beginning-of-def-or-class]\t move to start of class +\\[py-end-of-def-or-class]\t move to end of def +\\[universal-argument] \\[py-end-of-def-or-class]\t move to end of class + +The first two move to one statement beyond the statement that contains +point. A numeric prefix argument tells them to move that many +statements instead. Blank lines, comment lines, and continuation lines +do not count as `statements' for these commands. So, e.g., you can go +to the first code statement in a file by entering +\t\\[beginning-of-buffer]\t to move to the top of the file +\t\\[py-next-statement]\t to skip over initial comments and blank lines +Or do `\\[py-previous-statement]' with a huge prefix argument. +%c:py-previous-statement +%c:py-next-statement +%c:py-goto-block-up +%c:py-beginning-of-def-or-class +%c:py-end-of-def-or-class + +@LITTLE-KNOWN EMACS COMMANDS PARTICULARLY USEFUL IN PYTHON MODE + +`\\[indent-new-comment-line]' is handy for entering a multi-line comment. + +`\\[set-selective-display]' with a `small' prefix arg is ideally suited for viewing the +overall class and def structure of a module. + +`\\[back-to-indentation]' moves point to a line's first non-blank character. + +`\\[indent-relative]' is handy for creating odd indentation. + +@OTHER EMACS HINTS + +If you don't like the default value of a variable, change its value to +whatever you do like by putting a `setq' line in your .emacs file. +E.g., to set the indentation increment to 4, put this line in your +.emacs: +\t(setq py-indent-offset 4) +To see the value of a variable, do `\\[describe-variable]' and enter the variable +name at the prompt. + +When entering a key sequence like `C-c C-n', it is not necessary to +release the CONTROL key after doing the `C-c' part -- it suffices to +press the CONTROL key, press and release `c' (while still holding down +CONTROL), press and release `n' (while still holding down CONTROL), & +then release CONTROL. + +Entering Python mode calls with no arguments the value of the variable +`python-mode-hook', if that value exists and is not nil; for backward +compatibility it also tries `py-mode-hook'; see the `Hooks' section of +the Elisp manual for details. + +Obscure: When python-mode is first loaded, it looks for all bindings +to newline-and-indent in the global keymap, and shadows them with +local bindings to py-newline-and-indent.")) + +;; (require 'info-look) +;; The info-look package does not always provide this function (it +;; appears this is the case with XEmacs 21.1) +(when (fboundp 'info-lookup-maybe-add-help) + (info-lookup-maybe-add-help + :mode 'python-mode + :regexp "[a-zA-Z0-9_]+" + :doc-spec '(("(python-lib)Module Index") + ("(python-lib)Class-Exception-Object Index") + ("(python-lib)Function-Method-Variable Index") + ("(python-lib)Miscellaneous Index")))) + +(defvar python-preoutput-result nil + "Data from last `_emacs_out' line seen by the preoutput filter.") + +(defun py-send-receive (string) + "Send STRING to inferior Python (if any) and return result. + +The result is what follows `_emacs_out' in the output. +This is a no-op if `python-check-comint-prompt' returns nil." + (python-shell-send-string-no-output string) + (let ((proc (py-proc))) + (with-current-buffer (process-buffer proc) + (when (python-check-comint-prompt proc) + (set (make-local-variable 'python-preoutput-result) nil) + (accept-process-output proc 5) + (prog1 python-preoutput-result + (kill-local-variable 'python-preoutput-result)))))) + +(defun py-find-function (name) + "Find source of definition of function NAME. + +Interactively, prompt for name." + (interactive + (let ((symbol (with-syntax-table py-dotted-expression-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 (py-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-char (point-min)) + (forward-line (1- line))))) + +(defun py-update-imports () + "Returns `python-imports'. + +Imports done are displayed in message buffer. " + (interactive) + (save-excursion + (let ((oldbuf (current-buffer)) + (orig (point)) + erg) + (mapc 'py-execute-string (split-string (car (read-from-string (py-find-imports))) "\n" t)) + (setq erg (car (read-from-string python-imports))) + (set-buffer oldbuf) + (goto-char orig) + (when (interactive-p) + (switch-to-buffer (current-buffer)) + (when py-verbose-p (message "%s" erg))) + erg))) + +;;; python-components-extensions.el + +(defun py-indent-forward-line (&optional arg) + "Indent and move one line forward to next indentation. +Returns column of line reached. + +If `py-kill-empty-line' is non-nil, delete an empty line. +When closing a form, use py-close-block et al, which will move and indent likewise. +With \\[universal argument] just indent. +" + (interactive "*P") + (let ((orig (point)) + erg) + (unless (eobp) + (if (and (py-in-comment-p)(not py-indent-comments)) + (forward-line 1) + (py-indent-line-outmost) + (unless (eq 4 (prefix-numeric-value arg)) + (if (eobp) (newline) + (progn (forward-line 1)) + (when (and py-kill-empty-line (empty-line-p) (not (looking-at "[ \t]*\n[[:alpha:]]")) (not (eobp))) + (delete-region (line-beginning-position) (line-end-position))))))) + (back-to-indentation) + (when (or (eq 4 (prefix-numeric-value arg)) (< orig (point))) (setq erg (current-column))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-dedent-forward-line (&optional arg) + "Dedent line and move one line forward. " + (interactive "*p") + (py-dedent arg) + (forward-line 1) + (end-of-line) + (skip-chars-backward " \t\r\n\f")) + +(defun py-dedent (&optional arg) + "Dedent line according to `py-indent-offset'. + +With arg, do it that many times. +If point is between indent levels, dedent to next level. +Return indentation reached, if dedent done, nil otherwise. + +Affected by `py-dedent-keep-relative-column'. " + (interactive "*p") + (let ((orig (copy-marker (point))) + erg) + (dotimes (i arg) + (let* ((cui (current-indentation)) + (remain (% cui py-indent-offset)) + (indent (* py-indent-offset (/ cui py-indent-offset)))) + (beginning-of-line) + (fixup-whitespace) + (if (< 0 remain) + (indent-to-column indent) + (indent-to-column (- cui py-indent-offset))))) + (when (< (point) orig) + (setq erg (current-column))) + (if py-dedent-keep-relative-column + (goto-char orig) + (end-of-line) + (skip-chars-backward " \t\r\n\f")) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-close-intern (regexp) + "Core function, internal used only. " + (let ((cui (ignore-errors (car (py-go-to-keyword regexp))))) + (py-end-base regexp (point)) + (forward-line 1) + (if py-close-provides-newline + (unless (empty-line-p) (split-line)) + (fixup-whitespace)) + (indent-to-column cui) + cui)) + +(defun py-close-def () + "Set indent level to that of beginning of function definition. + +If final line isn't empty and `py-close-block-provides-newline' non-nil, insert a newline. " + (interactive "*") + (let ((erg (py-close-intern py-def-re))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-close-class () + "Set indent level to that of beginning of class definition. + +If final line isn't empty and `py-close-block-provides-newline' non-nil, insert a newline. " + (interactive "*") + (let ((erg (py-close-intern py-class-re))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-close-clause () + "Set indent level to that of beginning of clause definition. + +If final line isn't empty and `py-close-block-provides-newline' non-nil, insert a newline. " + (interactive "*") + (let ((erg (py-close-intern py-clause-re))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-close-block () + "Set indent level to that of beginning of block definition. + +If final line isn't empty and `py-close-block-provides-newline' non-nil, insert a newline. " + (interactive "*") + (let ((erg (py-close-intern py-block-re))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-class-at-point () + "Return class definition as string. + +With interactive call, send it to the message buffer too. " + (interactive) + (save-excursion + (let* ((beg (py-beginning-of-class)) + (end (py-end-of-class)) + (res (when (and (numberp beg)(numberp end)(< beg end)) (buffer-substring-no-properties beg end)))) + (when (and py-verbose-p (interactive-p)) (message "%s" res)) + res))) + +(defun py-line-at-point () + "Return line as string. + With interactive call, send it to the message buffer too. " + (interactive) + (let* ((beg (line-beginning-position)) + (end (line-end-position)) + (res (when (and (numberp beg)(numberp end)(< beg end)) (buffer-substring-no-properties beg end)))) + (when (and py-verbose-p (interactive-p)) (message "%s" res)) + res)) + +(defun py-looking-at-keywords-p () + "If looking at a python keyword. Returns t or nil. " + (interactive) + (let* ((kwds1 (car (nth 1 (eval (eval (quote (car font-lock-defaults))))))) + (kwds3 (car (nth 3 (eval (eval (quote (car font-lock-defaults))))))) + (res + (or + (looking-at kwds1) + (looking-at kwds3)))) + (when (and py-verbose-p (interactive-p)) (message "looking-at keywords: %s" res)) + res)) + +(defun py-match-paren-mode (&optional arg) + "py-match-paren-mode nil oder t" + (interactive "P") + (if (or (eq 4 (prefix-numeric-value arg)) (not py-match-paren-mode)) + (setq py-match-paren-mode t) + (setq py-match-paren-mode nil))) + +(defun py-match-paren () + "Goto to the opening or closing of block before or after point. + +With arg, do it that many times. + Closes unclosed block if jumping from beginning. " + (interactive) + (let ((cuc (current-column)) + (cid (current-indentation))) + (py-beginning-of-block-or-clause) + (if (< cuc (current-indentation)) + (goto-char cuc) + (back-to-indentation) + (when (eq (point) cuc) + (py-end-of-block))))) + +;;; from string-strip.el --- Strip CHARS from STRING + +;; (setq strip-chars-before "[ \t\r\n]*") +(defcustom strip-chars-before "[ \t\r\n]*" + "Regexp indicating which chars shall be stripped before STRING - which is defined by `string-chars-preserve'." + + :type 'string + :group 'convenience) + +;; (setq strip-chars-after "[ \t\r\n]*") +(defcustom strip-chars-after "[ \t\r\n]*\\'" + "Regexp indicating which chars shall be stripped after STRING - which is defined by `string-chars-preserve'." + + :type 'string + :group 'convenience) + +(defcustom string-chars-preserve "\\(.*?\\)" + "Chars preserved of STRING. +`strip-chars-after' and +`strip-chars-before' indicate what class of chars to strip." + :type 'string + :group 'convenience) + +(defun string-strip (str &optional chars-before chars-after chars-preserve) + "Return a copy of STR, CHARS removed. +`CHARS-BEFORE' and `CHARS-AFTER' default is \"[ \t\r\n]*\", +i.e. spaces, tabs, carriage returns, newlines and newpages. +`CHARS-PRESERVE' must be a parentized expression, +it defaults to \"\\(.*?\\)\"" + (let ((s-c-b (or chars-before + strip-chars-before)) + (s-c-a (or chars-after + strip-chars-after)) + (s-c-p (or chars-preserve + string-chars-preserve))) + (string-match + (concat "\\`[" s-c-b"]*" s-c-p "[" s-c-a "]*\\'") str) + (match-string 1 str))) + +;;; + +(defalias 'druck 'py-printform-insert) +(defun py-printform-insert (&optional arg) + "Inserts a print statement out of current `(car kill-ring)' by default, inserts ARG instead if delivered. " + (interactive "*") + (let* ((name (string-strip (or arg (car kill-ring)))) + (form (cond ((or (eq major-mode 'python-mode)(eq major-mode 'inferior-python-mode)) + (concat "print \"" name ": %s \" % " name))))) + (insert form))) + +(defun py-documentation (w) + "Launch PyDOC on the Word at Point" + (interactive + (list (let* ((word (thing-at-point 'word)) + (input (read-string + (format "pydoc entry%s: " + (if (not word) "" (format " (default %s)" word)))))) + (if (string= input "") + (if (not word) (error "No pydoc args given") + word) ;sinon word + input)))) ;sinon input + (shell-command (concat py-shell-name " -c \"from pydoc import help;help(\'" w "\')\"") "*PYDOCS*") + (view-buffer-other-window "*PYDOCS*" t 'kill-buffer-and-window)) + +(defun eva () + "Put \"eval(...)\" forms around strings at point. " + (interactive "*") + (skip-chars-forward " \t\r\n\f") + (let* ((bounds (ar-bounds-of-word-atpt)) + (beg (car bounds)) + (end (cdr bounds))) + (goto-char end) + (insert ")") + (goto-char beg) + (insert "eval("))) + +(defun pst-here () + "Kill previous \"pdb.set_trace()\" and insert it at point. " + (interactive "*") + (let ((orig (copy-marker (point)))) + (search-backward "pdb.set_trace()") + (replace-match "") + (when (empty-line-p) + (delete-region (line-beginning-position) (line-end-position))) + (goto-char orig) + (insert "pdb.set_trace()"))) + +(defun py-line-to-printform-python2 (&optional arg) + "Transforms the item on current in a print statement. " + (interactive "*") + (let* ((name (thing-at-point 'word)) + (form (cond ((or (eq major-mode 'python-mode)(eq major-mode 'inferior-python-mode)) + (concat "print \"" name ": %s \" % " name))))) + (delete-region (line-beginning-position) (line-end-position)) + (insert form)) + (forward-line 1) + (back-to-indentation)) + +;;; python-components-imenu.el + +;; Imenu definitions +(defvar py-imenu-class-regexp + (concat ; <<classes>> + "\\(" ; + "^[ \t]*" ; newline and maybe whitespace + "\\(class[ \t]+[a-zA-Z0-9_]+\\)" ; class name + ; possibly multiple superclasses + "\\([ \t]*\\((\\([a-zA-Z0-9_,. \t\n]\\)*)\\)?\\)" + "[ \t]*:" ; and the final : + "\\)" ; >>classes<< + ) + "Regexp for Python classes for use with the Imenu package.") + +(defvar py-imenu-method-regexp + (concat ; <<methods and functions>> + "\\(" ; + "^[ \t]*" ; new line and maybe whitespace + "\\(def[ \t]+" ; function definitions start with def + "\\([a-zA-Z0-9_]+\\)" ; name is here + ; function arguments... + ;; "[ \t]*(\\([-+/a-zA-Z0-9_=,\* \t\n.()\"'#]*\\))" + "[ \t]*(\\([^:#]*\\))" + "\\)" ; end of def + "[ \t]*:" ; and then the : + "\\)" ; >>methods and functions<< + ) + "Regexp for Python methods/functions for use with the Imenu package.") + +(defvar py-imenu-method-no-arg-parens '(2 8) + "Indices into groups of the Python regexp for use with Imenu. + +Using these values will result in smaller Imenu lists, as arguments to +functions are not listed. + +See the variable `py-imenu-show-method-args-p' for more +information.") + +(defvar py-imenu-method-arg-parens '(2 7) + "Indices into groups of the Python regexp for use with imenu. +Using these values will result in large Imenu lists, as arguments to +functions are listed. + +See the variable `py-imenu-show-method-args-p' for more +information.") + +;; Note that in this format, this variable can still be used with the +;; imenu--generic-function. Otherwise, there is no real reason to have +;; it. +(defvar py-imenu-generic-expression + (cons + (concat + py-imenu-class-regexp + "\\|" ; or... + py-imenu-method-regexp) + py-imenu-method-no-arg-parens) + "Generic Python expression which may be used directly with Imenu. +Used by setting the variable `imenu-generic-expression' to this value. +Also, see the function \\[py-imenu-create-index] for a better +alternative for finding the index.") + +;; These next two variables are used when searching for the Python +;; class/definitions. Just saving some time in accessing the +;; generic-python-expression, really. +(defvar py-imenu-generic-regexp nil) +(defvar py-imenu-generic-parens nil) + +(defun py-switch-imenu-index-function () + "For development only. Good old renamed `py-imenu-create-index'-function hangs with medium size files already. Working `py-imenu-create-index-new' is active by default. + +Switch between classic index machine `py-imenu-create-index'-function and new `py-imenu-create-index-new'. + +The former may provide a more detailed report, thus delivering two different index-machines is considered. " + (interactive) + (if (eq major-mode 'python-mode) + (progn + (if (eq imenu-create-index-function 'py-imenu-create-index-new) + (setq imenu-create-index-function #'py-imenu-create-index) + (setq imenu-create-index-function #'py-imenu-create-index-new)) + (when (and py-verbose-p (interactive-p)) (message "imenu-create-index-function: %s" (prin1-to-string imenu-create-index-function)))) + (error "%s" "Only available in buffers set to python-mode"))) + +(defun py-imenu-create-index-function () + "Python interface function for the Imenu package. +Finds all Python classes and functions/methods. Calls function +\\[py-imenu-create-index-engine]. See that function for the details +of how this works." + (setq py-imenu-generic-regexp (car py-imenu-generic-expression) + py-imenu-generic-parens (if py-imenu-show-method-args-p + py-imenu-method-arg-parens + py-imenu-method-no-arg-parens)) + (goto-char (point-min)) + ;; Warning: When the buffer has no classes or functions, this will + ;; return nil, which seems proper according to the Imenu API, but + ;; causes an error in the XEmacs port of Imenu. Sigh. + (py-imenu-create-index-engine nil)) + +(defun py-imenu-create-index-engine (&optional start-indent) + "Function for finding Imenu definitions in Python. + +Finds all definitions (classes, methods, or functions) in a Python +file for the Imenu package. + +Returns a possibly nested alist of the form + + (INDEX-NAME . INDEX-POSITION) + +The second element of the alist may be an alist, producing a nested +list as in + + (INDEX-NAME . INDEX-ALIST) + +This function should not be called directly, as it calls itself +recursively and requires some setup. Rather this is the engine for +the function \\[py-imenu-create-index-function]. + +It works recursively by looking for all definitions at the current +indention level. When it finds one, it adds it to the alist. If it +finds a definition at a greater indentation level, it removes the +previous definition from the alist. In its place it adds all +definitions found at the next indentation level. When it finds a +definition that is less indented then the current level, it returns +the alist it has created thus far. + +The optional argument START-INDENT indicates the starting indentation +at which to continue looking for Python classes, methods, or +functions. If this is not supplied, the function uses the indentation +of the first definition found." + (let (index-alist + sub-method-alist + looking-p + def-name prev-name + cur-indent def-pos + (class-paren (first py-imenu-generic-parens)) + (def-paren (second py-imenu-generic-parens))) + (setq looking-p + (re-search-forward py-imenu-generic-regexp (point-max) t)) + (while looking-p + (save-excursion + ;; used to set def-name to this value but generic-extract-name + ;; is new to imenu-1.14. this way it still works with + ;; imenu-1.11 + ;;(imenu--generic-extract-name py-imenu-generic-parens)) + (let ((cur-paren (if (match-beginning class-paren) + class-paren def-paren))) + (setq def-name + (buffer-substring-no-properties (match-beginning cur-paren) + (match-end cur-paren)))) + (save-match-data + (py-beginning-of-def-or-class)) + (beginning-of-line) + (setq cur-indent (current-indentation))) + ;; HACK: want to go to the next correct definition location. We + ;; explicitly list them here but it would be better to have them + ;; in a list. + (setq def-pos + (or (match-beginning class-paren) + (match-beginning def-paren))) + ;; if we don't have a starting indent level, take this one + (or start-indent + (setq start-indent cur-indent)) + ;; if we don't have class name yet, take this one + (or prev-name + (setq prev-name def-name)) + ;; what level is the next definition on? must be same, deeper + ;; or shallower indentation + (cond + ;; Skip code in comments and strings + ((py-in-literal)) + ;; at the same indent level, add it to the list... + ((= start-indent cur-indent) + (push (cons def-name def-pos) index-alist)) + ;; deeper indented expression, recurse + ((< start-indent cur-indent) + ;; the point is currently on the expression we're supposed to + ;; start on, so go back to the last expression. The recursive + ;; call will find this place again and add it to the correct + ;; list + (re-search-backward py-imenu-generic-regexp (point-min) 'move) + (setq sub-method-alist (py-imenu-create-index-engine cur-indent)) + (if sub-method-alist + ;; we put the last element on the index-alist on the start + ;; of the submethod alist so the user can still get to it. + (let ((save-elmt (pop index-alist))) + (push (cons prev-name + (cons save-elmt sub-method-alist)) + index-alist)))) + ;; found less indented expression, we're done. + (t + (setq looking-p nil) + (re-search-backward py-imenu-generic-regexp (point-min) t))) + ;; end-cond + (setq prev-name def-name) + (and looking-p + (setq looking-p + (re-search-forward py-imenu-generic-regexp + (point-max) 'move)))) + (nreverse index-alist))) + +(defvar imenu-max-items) +(defun py-imenu-create-index-new-intern (&optional thisend) + (let* ((pos (match-beginning 0)) + (name (match-string-no-properties 2)) + (classname (concat "class " name)) + (thisend (or thisend (save-match-data (py-end-of-def-or-class-position)))) + sublist) + (while (and (re-search-forward "^[ \t]*\\(?:\\(def\\|class\\)\\)[ \t]+\\(?:\\(\\sw+\\)\\)" (or thisend end) t 1)(not (nth 8 (syntax-ppss)))) + (let* ((pos (match-beginning 0)) + (name (match-string-no-properties 2)) + (classname (concat "class " name)) + (thisend (or thisend (save-match-data (py-end-of-def-or-class-position))))) + (if (string= "class" (match-string-no-properties 1)) + (py-imenu-create-index-new-intern (save-match-data (py-end-of-def-or-class-position))) + (push (cons (concat " " name) pos) sublist)))) + (if classname + (progn + (setq sublist (nreverse sublist)) + (push (cons classname pos) sublist) + (push (cons classname sublist) index-alist)) + (push sublist index-alist)))) + +(defun py-imenu-create-index-new (&optional beg end) + "`imenu-create-index-function' for Python. " + (set (make-local-variable 'imenu-max-items) 99) + (let ((orig (point)) + (beg (or beg (point-min))) + (end (or end (point-max))) + index-alist vars thisend sublist classname) + (goto-char beg) + (while (and (re-search-forward "^[ \t]*\\(def\\|class\\)[ \t]+\\(\\sw+\\)" end t 1)(not (nth 8 (syntax-ppss)))) + (if (save-match-data (string= "class" (match-string-no-properties 1))) + (progn + (setq pos (match-beginning 0) + name (match-string-no-properties 2) + classname (concat "class " name) + thisend (save-match-data (py-end-of-def-or-class-position)) + sublist '()) + (while (and (re-search-forward "^[ \t]*\\(def\\|class\\)[ \t]+\\(\\sw+\\)" (or thisend end) t 1)(not (nth 8 (syntax-ppss)))) + (let* ((pos (match-beginning 0)) + (name (match-string-no-properties 2)) + (classname (concat "class " name)) + (thisend (or thisend (save-match-data (py-end-of-def-or-class-position))))) + (if (string= "class" (match-string-no-properties 1)) + (py-imenu-create-index-new-intern (save-match-data (py-end-of-def-or-class-position))) + (push (cons (concat " " name) pos) sublist)))) + (if classname + (progn + (setq sublist (nreverse sublist)) + (push (cons classname pos) sublist) + (push (cons classname sublist) index-alist)) + (push sublist index-alist))) + + (let ((pos (match-beginning 0)) + (name (match-string-no-properties 2))) + (push (cons name pos) index-alist)))) + ;; Look for module variables. + (goto-char (point-min)) + (while (re-search-forward "^\\(\\sw+\\)[ \t]*=" end t) + (unless (nth 8 (syntax-ppss)) + (let ((pos (match-beginning 1)) + (name (match-string-no-properties 1))) + (push (cons name pos) vars)))) + (setq index-alist (nreverse index-alist)) + (when vars + (push (cons "Module variables" + (nreverse vars)) + index-alist)) + (goto-char orig) + index-alist)) + +;;; python-components-completion.el +(defun python-symbol-completions (symbol) + "Return a list of completions of the string SYMBOL from Python process. +The list is sorted. +Uses `python-imports' to load modules against which to complete." + (when (stringp symbol) + (let ((completions + (condition-case () + (car (read-from-string + (python-send-receive + (format "emacs.complete(%S,%s)" + (substring-no-properties symbol) + python-imports)))) + (error nil)))) + (sort + ;; We can get duplicates from the above -- don't know why. + (delete-dups completions) + #'string<)))) + +(defvar py-mode-output-map nil + "Keymap used in *Python Output* buffers.") +(if py-mode-output-map + nil + (setq py-mode-output-map (make-sparse-keymap)) + (define-key py-mode-output-map [button2] 'py-mouseto-exception) + (define-key py-mode-output-map "\C-c\C-c" 'py-goto-exception) + ;; TBD: Disable all self-inserting keys. This is bogus, we should + ;; really implement this as *Python Output* buffer being read-only + (mapc #' (lambda (key) + (define-key py-mode-output-map key + #'(lambda () (interactive) (beep)))) + (where-is-internal 'self-insert-command))) + +(setq py-shell-map + (let ((map (copy-keymap comint-mode-map))) + (define-key map (kbd "RET") 'comint-send-input) + (define-key map "\C-c-" 'py-up-exception) + (define-key map "\C-c=" 'py-down-exception) + ;; defined three times... one should succed + (define-key map (kbd "TAB") 'py-shell-complete) + (define-key map [(meta tab)] 'py-shell-complete) + map)) + +(defun py-choose-shell-by-path (&optional file-separator-char) + "Select Python executable according to version desplayed in path, current buffer-file is selected from. + +Returns versioned string, nil if nothing appropriate found " + (interactive) + (lexical-let ((path (buffer-file-name)) + (file-separator-char (or file-separator-char (char-to-string py-separator-char))) + erg) + (when (and path file-separator-char + (string-match (concat file-separator-char "[iI]?[pP]ython[0-9.]+" file-separator-char) path)) + (setq erg (substring path + (1+ (string-match (concat file-separator-char "[iI]?[pP]ython[0-9.]+" file-separator-char) path)) (1- (match-end 0)) ))) + (when (interactive-p) (message "%s" erg)) + erg)) + +(defun py-choose-shell-by-shebang () + "Choose shell by looking at #! on the first line. + +Returns the specified Python resp. Jython shell command name. " + (interactive) + ;; look for an interpreter specified in the first line + (let* (erg res) + (save-excursion + (goto-char (point-min)) + (when (looking-at py-shebang-regexp) + (setq erg (split-string (match-string-no-properties 0) "[#! \t]")) + (dolist (ele erg) + (when (string-match "[bijp]+ython" ele) + (setq res ele))))) + (when (and py-verbose-p (interactive-p)) (message "%s" res)) + res)) + +(defun py-choose-shell-by-import () + "Choose CPython or Jython mode based imports. + +If a file imports any packages in `py-jython-packages', within +`py-import-check-point-max' characters from the start of the file, +return `jython', otherwise return nil." + (let (mode) + (save-excursion + (goto-char (point-min)) + (while (and (not mode) + (search-forward-regexp + "^\\(\\(from\\)\\|\\(import\\)\\) \\([^ \t\n.]+\\)" + py-import-check-point-max t)) + (setq mode (and (member (match-string 4) py-jython-packages) + 'jython)))) + mode)) + +(defalias 'py-version 'py-which-python) +(defun py-which-python () + "Returns version of Python of current environment, a number. " + (interactive) + (let* ((cmd (py-choose-shell)) + (erg (shell-command-to-string (concat cmd " --version"))) + ;; Result: "bpython version 0.9.7.1 on top of Python 2.7\n(C) 2008-2010 Bob Farrell, Andreas Stuehrk et al. See AUTHORS for detail.\n" + + (version (cond ((string-match (concat "\\(on top of Python \\)" "\\([0-9]\\.[0-9]+\\)") erg) + (match-string-no-properties 2 erg)) + ((string-match "\\([0-9]\\.[0-9]+\\)" erg) + (substring erg 7 (1- (length erg))))))) + (when (interactive-p) + (if erg + (when py-verbose-p (message "%s" erg)) + (message "%s" "Could not detect Python on your system"))) + (string-to-number version))) + +(defun py-python-current-environment () + "Returns path of current Python installation. " + (interactive) + (let* ((cmd (py-choose-shell)) + (denv (shell-command-to-string (concat "type " cmd))) + (erg (substring denv (string-match "/" denv)))) + (when (interactive-p) + (if erg + (when py-verbose-p (message "%s" erg)) + (message "%s" "Could not detect Python on your system"))) + erg)) + +(defalias 'python-toggle-shells 'py-switch-shell) +(defalias 'py-toggle-shell 'py-switch-shell) +(defun py-switch-shell (&optional arg) + "Toggles between the interpreter customized in `py-shell-toggle-1' resp. `py-shell-toggle-2'. Was hard-coded CPython and Jython in earlier versions, now starts with Python2 and Python3 by default. + +ARG might be a python-version string to set to. + +\\[universal-argument] `py-toggle-shell' prompts to specify a reachable Python command. +\\[universal-argument] followed by numerical arg 2 or 3, `py-toggle-shell' opens a respective Python shell. +\\[universal-argument] followed by numerical arg 5 opens a Jython shell. + +Should you need more shells to select, extend this command by adding inside the first cond: + + ((eq NUMBER (prefix-numeric-value arg)) + \"MY-PATH-TO-SHELL\") +" + (interactive "P") + (let ((name (cond ((eq 2 (prefix-numeric-value arg)) + "python2") + ((eq 3 (prefix-numeric-value arg)) + "python3") + ((eq 4 (prefix-numeric-value arg)) + (string-strip + (read-from-minibuffer "Python Shell: " py-shell-name) "\" " "\" " + )) + ((eq 5 (prefix-numeric-value arg)) + "jython") + (t (if (string-match py-shell-name + py-shell-toggle-1) + py-shell-toggle-2 + py-shell-toggle-1)))) + erg) + (cond ((or (string= "ipython" name) + (string= "IPython" name)) + (setq py-shell-name name + py-which-bufname "IPython" + msg "IPython" + mode-name "IPython")) + ((string-match "python3" name) + (setq py-shell-name name + py-which-bufname (py-buffer-name-prepare name) + msg "CPython" + mode-name (py-buffer-name-prepare name))) + ((string-match "jython" name) + (setq py-shell-name name + py-which-bufname (py-buffer-name-prepare name) + msg "Jython" + mode-name (py-buffer-name-prepare name))) + ((string-match "python" name) + (setq py-shell-name name + py-which-bufname (py-buffer-name-prepare name) + msg "CPython" + mode-name py-which-bufname)) + (t + (setq py-shell-name name + py-which-bufname name + msg name + mode-name name))) + ;; py-edit-only-p has no interpreter + ;; (if py-edit-only-p + ;; (setq erg py-shell-name) + (setq erg (executable-find py-shell-name)) + ;;) + (if erg + (progn + (force-mode-line-update) + (when (interactive-p) + (message "Using the %s shell, %s" msg erg)) + (setq py-output-buffer (format "*%s Output*" py-which-bufname))) + (error (concat "Could not detect " py-shell-name " on your sys +tem"))))) + +(defalias 'py-which-shell 'py-choose-shell) +(defun py-choose-shell (&optional arg pyshell dedicated) + "Return an appropriate executable as a string. + +Returns nil, if no executable found. + +This does the following: + - look for an interpreter with `py-choose-shell-by-shebang' + - examine imports using `py-choose-shell-by-import' + - look if Path/To/File indicates a Python version + - if not successful, return default value of `py-shell-name' + +When interactivly called, messages the shell name, Emacs would in the given circtumstances. + +With \\[universal-argument] 4 is called `py-switch-shell' see docu there. +" + (interactive "P") + (if (eq 4 (prefix-numeric-value arg)) + (py-switch-shell '(4)) + (let* ((erg (cond (py-force-py-shell-name-p + py-shell-name) + (py-use-local-default + (if (not (string= "" py-shell-local-path)) + (expand-file-name py-shell-local-path) + (message "Abort: `py-use-local-default' is set to `t' but `py-shell-local-path' is empty. Maybe call `py-toggle-local-default-use'"))) + ((comint-check-proc (current-buffer)) + (process-name (get-buffer-process (current-buffer)))) + ((py-choose-shell-by-shebang)) + ((py-choose-shell-by-import)) + ((py-choose-shell-by-path)) + (py-shell-name py-shell-name) + (t (default-value 'py-shell-name)))) + (cmd (if py-edit-only-p erg + (executable-find erg)))) + (if cmd + (when (interactive-p) + (message "%s" cmd)) + (when (interactive-p) (message "%s" "Could not detect Python on your system. Maybe set `py-edit-only-p'?"))) + erg))) + +(defalias 'toggle-py-smart-indentation 'py-toggle-smart-indentation) +(defun py-toggle-smart-indentation (&optional arg) + "If `py-smart-indentation' should be on or off. + +Returns value of `py-smart-indentation' switched to. " + (interactive) + (let ((arg (or arg (if py-smart-indentation -1 1)))) + (if (< 0 arg) + (setq py-smart-indentation t) + (setq py-smart-indentation nil) + (setq py-indent-offset (default-value 'py-indent-offset))) + (when (and py-verbose-p (interactive-p)) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation)) + +(defun py-smart-indentation-on (&optional arg) + "Make sure, `py-smart-indentation' is on. + +Returns value of `py-smart-indentation'. " + (interactive "p") + (let ((arg (or arg 1))) + (toggle-py-smart-indentation arg)) + (when (interactive-p) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation) + +(defun py-smart-indentation-off (&optional arg) + "Make sure, `py-smart-indentation' is off. + +Returns value of `py-smart-indentation'. " + (interactive "p") + (let ((arg (if arg (- arg) -1))) + (toggle-py-smart-indentation arg)) + (when (interactive-p) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation) + +;;; Split-Windows-On-Execute forms +(defalias 'toggle-py-split-windows-on-execute 'py-toggle-split-windows-on-execute) +(defun py-toggle-split-windows-on-execute (&optional arg) + "If `py-split-windows-on-execute-p' should be on or off. + + Returns value of `py-split-windows-on-execute-p' switched to. " + (interactive) + (let ((arg (or arg (if py-split-windows-on-execute-p -1 1)))) + (if (< 0 arg) + (setq py-split-windows-on-execute-p t) + (setq py-split-windows-on-execute-p nil)) + (when (interactive-p) (message "py-split-windows-on-execute-p: %s" py-split-windows-on-execute-p)) + py-split-windows-on-execute-p)) + +(defun py-split-windows-on-execute-on (&optional arg) + "Make sure, `py-split-windows-on-execute-p' is on. + +Returns value of `py-split-windows-on-execute-p'. " + (interactive "p") + (let ((arg (or arg 1))) + (toggle-py-split-windows-on-execute arg)) + (when (interactive-p) (message "py-split-windows-on-execute-p: %s" py-split-windows-on-execute-p)) + py-split-windows-on-execute-p) + +(defun py-split-windows-on-execute-off () + "Make sure, `py-split-windows-on-execute-p' is off. + +Returns value of `py-split-windows-on-execute-p'. " + (interactive) + (toggle-py-split-windows-on-execute -1) + (when (interactive-p) (message "py-split-windows-on-execute-p: %s" py-split-windows-on-execute-p)) + py-split-windows-on-execute-p) + +;;; Flymake +(defun clear-flymake-allowed-file-name-masks (&optional suffix) + "Remove entries with SUFFIX from `flymake-allowed-file-name-masks'. + +Default is \"\\.py\\'\" " + (interactive "P") + (let ((suffix (cond ((eq 4 (prefix-numeric-value suffix)) + (read-from-minibuffer "Suffix: " "\\\\.py\\\\'")) + (suffix suffix) + (t "\\\\.py\\\\'"))) + (erg flymake-allowed-file-name-masks) + (newlist '())) + (dolist (ele flymake-allowed-file-name-masks) + (unless + ;; (string-match "\\\\.py\\\\'" (car ele)) + (string-match suffix (car ele)) + (add-to-list 'newlist ele t))) + (setq flymake-allowed-file-name-masks newlist) + (when (and py-verbose-p (interactive-p)) (message "%s" flymake-allowed-file-name-masks)) + flymake-allowed-file-name-masks)) + +(defun py-toggle-flymake-intern (name command) + ;; (clear-flymake-allowed-file-name-masks) + (unless (string-match "pyflakespep8" name) + (unless (executable-find name) + (when py-verbose-p (message "Don't see %s. Use `easy_install' %s? " name name)))) + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name)))) + (add-to-list 'flymake-allowed-file-name-masks (car (read-from-string (concat "(\"\\.py\\'\" flymake-" name ")")))) + (list command (list local-file)))) + +(defun pylint-flymake-mode () + "Toggle `pylint' `flymake-mode'. " + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pylint" "pylint") + (flymake-mode))) + +(defun pyflakes-flymake-mode () + "Toggle `pyflakes' `flymake-mode'. " + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pyflakes" "pyflakes") + (flymake-mode))) + +(defun pychecker-flymake-mode () + "Toggle `pychecker' `flymake-mode'. " + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pychecker" "pychecker") + (flymake-mode))) + +(defun pep8-flymake-mode () + "Toggle `pep8' `flymake-mode'. " + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pep8" "pep8") + (flymake-mode))) + +(defun pyflakespep8-flymake-mode () + "Toggle `pyflakespep8' `flymake-mode'. + +Joint call to pyflakes and pep8 as proposed by + +Keegan Carruthers-Smith + +" + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pyflakespep8" "pyflakespep8") + (flymake-mode))) + +;;; Shell-Switch-Buffers-On-Execute forms +(defalias 'toggle-py-shell-switch-buffers-on-execute 'py-toggle-shell-switch-buffers-on-execute) +(defun py-toggle-shell-switch-buffers-on-execute (&optional arg) + "If `py-switch-buffers-on-execute-p' should be on or off. + + Returns value of `py-switch-buffers-on-execute-p' switched to. " + (interactive) + (let ((arg (or arg (if py-switch-buffers-on-execute-p -1 1)))) + (if (< 0 arg) + (setq py-switch-buffers-on-execute-p t) + (setq py-switch-buffers-on-execute-p nil)) + (when (interactive-p) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p)) + +(defun py-shell-switch-buffers-on-execute-on (&optional arg) + "Make sure, `py-switch-buffers-on-execute-p' is on. + +Returns value of `py-switch-buffers-on-execute-p'. " + (interactive "p") + (let ((arg (or arg 1))) + (toggle-py-shell-switch-buffers-on-execute arg)) + (when (interactive-p) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +(defun py-shell-switch-buffers-on-execute-off () + "Make sure, `py-switch-buffers-on-execute-p' is off. + +Returns value of `py-switch-buffers-on-execute-p'. " + (interactive) + (toggle-py-shell-switch-buffers-on-execute -1) + (when (interactive-p) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +;;; +(defvar inferior-python-mode-map + (let ((map (make-sparse-keymap))) + ;; This will inherit from comint-mode-map. + (define-key map "\C-c\C-l" 'py-load-file) + (define-key map "\C-c\C-v" 'python-check) + ;; Note that we _can_ still use these commands which send to the + ;; Python process even at the prompt iff we have a normal prompt, + ;; i.e. '>>> ' and not '... '. See the comment before + ;; py-send-region. Fixme: uncomment these if we address that. + map)) + +(defun py-normalize-directory (directory &optional file-separator-char) + "Make sure DIRECTORY ends with a file-path separator char. + +Returns DIRECTORY" + (let* ((file-separator-char (or file-separator-char (char-to-string py-separator-char))) + (erg (cond ((string-match (concat file-separator-char "$") directory) + directory) + ((not (string= "" directory)) + (concat directory file-separator-char))))) + (unless erg (when py-verbose-p (message "Warning: directory is empty"))) + erg)) + +(defun py-install-directory-check () + "Do some sanity check for `py-install-directory'. + +Returns `t' if successful. " + (interactive) + (let ((erg (and (boundp 'py-install-directory) (stringp py-install-directory) (< 1 (length py-install-directory))))) + (when (interactive-p) (message "py-install-directory-check: %s" erg)) + erg)) + +(defun py-guess-py-install-directory () + "Takes value of user directory aka $HOME +if `(locate-library \"python-mode\")' is not succesful. " + (interactive) + (let ((erg (file-name-directory (locate-library "python-mode")))) + (if erg + (progn + (setq py-install-directory erg) + (when (and py-verbose-p (interactive-p)) (message "Setting py-install-directory to: %s" erg))) + (setq py-install-directory (expand-file-name "~/"))) + py-install-directory )) + +(defun py-set-load-path () + "Include needed subdirs of python-mode directory. " + (interactive) + (let ((py-install-directory (py-normalize-directory py-install-directory (char-to-string py-separator-char)))) + (cond ((and (not (string= "" py-install-directory))(stringp py-install-directory)) + (add-to-list 'load-path (expand-file-name py-install-directory)) + (add-to-list 'load-path (concat (expand-file-name py-install-directory) "completion")) + (add-to-list 'load-path (concat (expand-file-name py-install-directory) "test")) + (add-to-list 'load-path (concat (expand-file-name py-install-directory) "tools"))) + ((when py-guess-py-install-directory-p + (let ((guessed-py-install-directory (py-guess-py-install-directory))) + (when guessed-py-install-directory + (add-to-list 'load-path guessed-py-install-directory))))) + (t (error "Please set `py-install-directory', see INSTALL")) + (when (interactive-p) (message "%s" load-path))))) + +;;; Autopair +;; Original author: Joao Tavora <joaotavora [at] gmail.com> +;; X-URL: http://autopair.googlecode.com + +;; variables +(defvar autopair-pair-criteria 'help-balance + "How to decide whether to pair opening brackets or quotes. + +Set this to 'always to always pair, or 'help-balance to be more +criterious when pairing.") + +(defvar autopair-skip-criteria 'help-balance + "How to decide whether to skip closing brackets or quotes. + +Set this to 'always to always skip, or 'help-balance to be more +criterious when skipping.") + +(defvar autopair-emulation-alist nil + "A dinamic keymap for autopair set mostly from the current + syntax table.") + +(unless (> emacs-major-version 23) + (defvar autopair-dont-activate nil + "Control activation of `autopair-global-mode'. + +Set this to a non-nil value to skip activation of `autopair-mode' +in certain contexts. If however the value satisfies `functionp' +and is a function of no arguments, the function is called and it is +the return value that decides.") + (make-variable-buffer-local 'autopair-dont-activate)) + +(defvar autopair-extra-pairs nil + "Extra pairs for which to use pairing. + +It's a Common-lisp-style even-numbered property list, each pair +of elements being of the form (TYPE , PAIRS). PAIRS is a mixed +list whose elements are cons cells, which look like cells look +like (OPENING . CLOSING). Autopair pairs these like +parenthesis. + +TYPE can be one of: + +:string : whereby PAIRS will be considered only when inside a + string literal + +:comment : whereby PAIRS will be considered only when inside a comment + +:code : whereby PAIRS will be considered only when outisde a + string and a comment. + +:everywhere : whereby PAIRS will be considered in all situations + +In Emacs-lisp, this might be useful + +(add-hook 'emacs-lisp-mode-hook + #'(lambda () + (setq autopair-extra-pairs `(:comment ((?`. ?')))))) + +Note that this does *not* work for single characters, +e.x. characters you want to behave as quotes. See the +docs/source comments for more details.") + +(make-variable-buffer-local 'autopair-extra-pairs) + +(defvar autopair-dont-pair `(:string (?') :comment (?')) + "Characters for which to skip any pairing behaviour. + +This variable overrides `autopair-pair-criteria' and +`autopair-extra-pairs'. It does not + (currently) affect the skipping behaviour. + +It's a Common-lisp-style even-numbered property list, each pair +of elements being of the form (TYPE , CHARS). CHARS is a list of +characters and TYPE can be one of: + +:string : whereby characters in CHARS will not be autopaired when + inside a string literal + +:comment : whereby characters in CHARS will not be autopaired when + inside a comment + +:never : whereby characters in CHARS won't even have their + bindings replaced by autopair's. This particular option + should be used for troubleshooting and requires + `autopair-mode' to be restarted to have any effect.") +(make-variable-buffer-local 'autopair-dont-pair) + +(defvar autopair-action nil + "Autopair action decided on by last interactive autopair command, or nil. + +When autopair decides on an action this is a list whose first +three elements are (ACTION PAIR POS-BEFORE). + +ACTION is one of `opening', `insert-quote', `skip-quote', +`backspace', `newline' or `paired-delimiter'. PAIR is the pair of +the `autopair-inserted' character, if applicable. POS-BEFORE is +value of point before action command took place .") + +(defvar autopair-wrap-action nil + "Autowrap action decided on by autopair, if any. + +When autopair decides on an action this is a list whose first +three elements are (ACTION PAIR POS-BEFORE REGION-BEFORE). + +ACTION can only be `wrap' currently. PAIR and POS-BEFORE +delimiter are as in `autopair-action'. REGION-BEFORE is a cons +cell with the bounds of the region before the command takes +place") + +(defvar autopair-handle-action-fns '() + "Autopair handlers to run *instead* of the default handler. + +Each element is a function taking three arguments (ACTION, PAIR +and POS-BEFORE), which are the three elements of the +`autopair-action' variable, which see. + +If non-nil, these functions are called *instead* of the single +function `autopair-default-handle-action', so use this variable +to specify special behaviour. To also run the default behaviour, +be sure to include `autopair-default-handle-action' in the +list, or call it from your handlers.") +(make-variable-buffer-local 'autopair-handle-action-fns) + +(defvar autopair-handle-wrap-action-fns '() + "Autopair wrap handlers to run *instead* of the default handler. + +Each element is a function taking four arguments (ACTION, PAIR, +POS-BEFORE and REGION-BEFORE), which are the three elements of the +`autopair-wrap-action' variable, which see. + +If non-nil, these functions are called *instead* of the single +function `autopair-default-handle-wrap-action', so use this +variable to specify special behaviour. To also run the default +behaviour, be sure to include `autopair-default-handle-wrap-action' in +the list, or call it in your handlers.") +(make-variable-buffer-local 'autopair-handle-wrap-action-fns) + +(defvar autopair-inserted nil + "Delimiter inserted by last interactive autopair command. + +This is calculated with `autopair-calculate-inserted', which see.") + +(defun autopair-calculate-inserted () + "Attempts to guess the delimiter the current command is inserting. + +For now, simply returns `last-command-event'" + last-command-event) + +;; minor mode and global mode +;; +(define-globalized-minor-mode autopair-global-mode autopair-mode autopair-on) + +(defun autopair-on () + (unless (or buffer-read-only + (and (not (minibufferp)) + (string-match "^ \\*" (buffer-name))) + (eq major-mode 'sldb-mode) + (and (boundp 'autopair-dont-activate) + autopair-dont-activate)) + (autopair-mode 1))) + +(define-minor-mode autopair-mode + "Automagically pair braces and quotes. " + nil " pair" nil + (cond (autopair-mode + ;; Setup the dynamic emulation keymap + ;; + (let ((map (make-sparse-keymap))) + (define-key map [remap delete-backward-char] 'autopair-backspace) + (define-key map [remap backward-delete-char-untabify] 'autopair-backspace) + (define-key map (kbd "<backspace>") 'autopair-backspace) + (define-key map [backspace] 'autopair-backspace) + (define-key map (kbd "DEL") 'autopair-backspace) + (define-key map [return] 'autopair-newline) + (define-key map (kbd "RET") 'autopair-newline) + (dotimes (char 256) ;; only searches the first 256 chars, + ;; TODO: is this enough/toomuch/stupid? + (unless (member char + (getf autopair-dont-pair :never)) + (let* ((syntax-entry (aref (syntax-table) char)) + (class (and syntax-entry + (syntax-class syntax-entry))) + (pair (and syntax-entry + (cdr syntax-entry)))) + (cond ((eq class (car (string-to-syntax "("))) + ;; syntax classes "opening parens" and "close parens" + (define-key map (string char) 'autopair-insert-opening) + (define-key map (string pair) 'autopair-skip-close-maybe)) + ((eq class (car (string-to-syntax "\""))) + ;; syntax class "string quote + (define-key map (string char) 'autopair-insert-or-skip-quote)) + ((eq class (car (string-to-syntax "$"))) + ;; syntax class "paired-delimiter" + ;; + ;; Apropos this class, see Issues 18, 25 and + ;; elisp info node "35.2.1 Table of Syntax + ;; Classes". The fact that it supresses + ;; syntatic properties in the delimited region + ;; dictates that deciding to autopair/autoskip + ;; can't really be as clean as the string + ;; delimiter. + ;; + ;; Apparently, only `TeX-mode' uses this, so + ;; the best is to bind this to + ;; `autopair-insert-or-skip-paired-delimiter' + ;; which defers any decision making to + ;; mode-specific post-command handler + ;; functions. + ;; + (define-key map (string char) 'autopair-insert-or-skip-paired-delimiter)))))) + ;; read `autopair-extra-pairs' + (dolist (pairs-list (remove-if-not #'listp autopair-extra-pairs)) + (dolist (pair pairs-list) + (define-key map (string (car pair)) 'autopair-extra-insert-opening) + (define-key map (string (cdr pair)) 'autopair-extra-skip-close-maybe))) + + (set (make-local-variable 'autopair-emulation-alist) (list (cons t map))) + (add-hook 'python-mode-hook + #'(lambda () + (setq autopair-handle-action-fns + (list #'autopair-default-handle-action + #'autopair-python-triple-quote-action))))) + (setq autopair-action nil) + (setq autopair-wrap-action nil) + (add-hook 'emulation-mode-map-alists 'autopair-emulation-alist 'append) + (add-hook 'post-command-hook 'autopair-post-command-handler nil 'local)) + (t + (remove-hook 'python-mode-hook + #'(lambda () + (setq autopair-handle-action-fns + (list #'autopair-default-handle-action + #'autopair-python-triple-quote-action)))) + (setq autopair-emulation-alist nil) + (remove-hook 'emulation-mode-map-alists 'autopair-emulation-alist) + (remove-hook 'post-command-hook 'autopair-post-command-handler 'local)))) + +;; helper functions +;; +(defun autopair-syntax-ppss () + "Calculate syntax info relevant to autopair. + +A list of four elements is returned: + +- SYNTAX-INFO is either the result `syntax-ppss' or the result of + calling `parse-partial-sexp' with the appropriate + bounds (previously calculated with `syntax-ppss'. + +- WHERE-SYM can be one of the symbols :string, :comment or :code. + +- QUICK-SYNTAX-INFO is always the result returned by `syntax-ppss'. + +- BOUNDS are the boudaries of the current string or comment if + we're currently inside one." + (let* ((quick-syntax-info (syntax-ppss)) + (string-or-comment-start (nth 8 quick-syntax-info))) + (cond (;; inside a string, recalculate + (nth 3 quick-syntax-info) + (list (parse-partial-sexp (1+ string-or-comment-start) (point)) + :string + quick-syntax-info + (cons string-or-comment-start + (condition-case nil + (scan-sexps string-or-comment-start 1) + (error nil))))) + ((nth 4 quick-syntax-info) + (list (parse-partial-sexp (1+ (nth 8 quick-syntax-info)) (point)) + :comment + quick-syntax-info)) + (t + (list quick-syntax-info + :code + quick-syntax-info))))) + +(defun autopair-find-pair (delim &optional closing) + (when (and delim + (integerp delim)) + (let ((syntax-entry (aref (syntax-table) delim))) + (cond ((eq (syntax-class syntax-entry) (car (string-to-syntax "("))) + (cdr syntax-entry)) + ((or (eq (syntax-class syntax-entry) (car (string-to-syntax "\""))) + (eq (syntax-class syntax-entry) (car (string-to-syntax "$")))) + delim) + ((and (not closing) + (eq (syntax-class syntax-entry) (car (string-to-syntax ")")))) + (cdr syntax-entry)) + (autopair-extra-pairs + (some #'(lambda (pair-list) + (some #'(lambda (pair) + (cond ((eq (cdr pair) delim) (car pair)) + ((eq (car pair) delim) (cdr pair)))) + pair-list)) + (remove-if-not #'listp autopair-extra-pairs))))))) + +(defun autopair-calculate-wrap-action () + (when (and transient-mark-mode mark-active) + (when (> (point) (mark)) + (exchange-point-and-mark)) + (save-excursion + (let* ((region-before (cons (region-beginning) + (region-end))) + (point-before (point)) + (start-syntax (syntax-ppss (car region-before))) + (end-syntax (syntax-ppss (cdr region-before)))) + (when (or (not (eq autopair-autowrap 'help-balance)) + (and (eq (nth 0 start-syntax) (nth 0 end-syntax)) + (eq (nth 3 start-syntax) (nth 3 end-syntax)))) + (list 'wrap (or (second autopair-action) + (autopair-find-pair autopair-inserted)) + point-before + region-before)))))) + +(defun autopair-original-binding (fallback-keys) + (or (key-binding `[,autopair-inserted]) + (key-binding (this-single-command-keys)) + (key-binding fallback-keys))) + +(defun autopair-fallback (&optional fallback-keys) + (let* ((autopair-emulation-alist nil) + (beyond-cua (let ((cua--keymap-alist nil)) + (autopair-original-binding fallback-keys))) + (beyond-autopair (autopair-original-binding fallback-keys))) + (when autopair-autowrap + (setq autopair-wrap-action (autopair-calculate-wrap-action))) + + (setq this-original-command beyond-cua) + ;; defer to "paredit-mode" if that is installed and running + (when (and (featurep 'paredit) + (string-match "paredit" (symbol-name beyond-cua))) + (setq autopair-action nil)) + (let ((cua-delete-selection (not autopair-autowrap)) + (blink-matching-paren (not autopair-action))) + (call-interactively beyond-autopair)))) + +(defvar autopair-autowrap 'help-balance + "If non-nil autopair attempts to wrap the selected region. + +This is also done in an optimistic \"try-to-balance\" fashion. +Set this to to 'help-balance to be more criterious when wrapping.") + +(defvar autopair-skip-whitespace nil + "If non-nil also skip over whitespace when skipping closing delimiters. + +If set to 'chomp, this will be most useful in lisp-like languages where you want +lots of )))))....") + +(defvar autopair-blink (if (boundp 'blink-matching-paren) + blink-matching-paren + t) + "If non-nil autopair blinks matching delimiters.") + +(defvar autopair-blink-delay 0.1 + "Autopair's blink-the-delimiter delay.") + +(defun autopair-document-bindings (&optional fallback-keys) + (concat + "Works by scheduling possible autopair behaviour, then calls +original command as if autopair didn't exist" + (when (eq this-command 'describe-key) + (let* ((autopair-emulation-alist nil) + (command (or (key-binding (this-single-command-keys)) + (key-binding fallback-keys)))) + (when command + (format ", which in this case is `%s'" command)))) + ".")) + +(defun autopair-escaped-p (syntax-info) + (nth 5 syntax-info)) + +(defun autopair-exception-p (where-sym exception-where-sym blacklist &optional fn) + (and (or (eq exception-where-sym :everywhere) + (eq exception-where-sym where-sym)) + (member autopair-inserted + (if fn + (mapcar fn (getf blacklist exception-where-sym)) + (getf blacklist exception-where-sym))))) + +(defun autopair-up-list (syntax-info &optional closing) + "Try to uplist as much as possible, moving point. + +Return nil if something prevented uplisting. + +Otherwise return a cons of char positions of the starting +delimiter and end delimiters of the last list we just came out +of. If we aren't inside any lists return a cons of current point. + +If inside nested lists of mixed parethesis types, finding a +matching parenthesis of a mixed-type is considered OK (non-nil is +returned) and uplisting stops there." + (condition-case nil + (let ((howmany (car syntax-info)) + (retval (cons (point) + (point)))) + (while (and (> howmany 0) + (condition-case err + (progn + (scan-sexps (point) (- (point-max))) + (error err)) + (error (let ((opening (and closing + (autopair-find-pair closing)))) + (setq retval (cons (fourth err) + (point))) + (or (not opening) + (eq opening (char-after (fourth err)))))))) + (goto-char (scan-lists (point) 1 1)) + (decf howmany)) + retval) + (error nil))) + +;; interactive commands and their associated predicates +;; +(defun autopair-insert-or-skip-quote () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (let* ((syntax-triplet (autopair-syntax-ppss)) + (syntax-info (first syntax-triplet)) + (where-sym (second syntax-triplet)) + (orig-info (third syntax-triplet)) + ;; inside-string may the quote character itself or t if this + ;; is a "generically terminated string" + (inside-string (and (eq where-sym :string) + (fourth orig-info))) + (escaped-p (autopair-escaped-p syntax-info)) + + ) + (cond (;; decides whether to skip the quote... + ;; + (and (not escaped-p) + (eq autopair-inserted (char-after (point))) + (or + ;; ... if we're already inside a string and the + ;; string starts with the character just inserted, + ;; or it's a generically terminated string + (and inside-string + (or (eq inside-string t) + (eq autopair-inserted inside-string))) + ;; ... if we're in a comment and ending a string + ;; (the inside-string criteria does not work + ;; here...) + (and (eq where-sym :comment) + (condition-case nil + (eq autopair-inserted (char-after (scan-sexps (1+ (point)) -1))) + (error nil))))) + (setq autopair-action (list 'skip-quote autopair-inserted (point)))) + (;; decides whether to pair, i.e do *not* pair the quote if... + ;; + (not + (or + escaped-p + ;; ... inside a generic string + (eq inside-string t) + ;; ... inside an unterminated string started by this char + (autopair-in-unterminated-string-p syntax-triplet) + ;; ... uplisting forward causes an error which leaves us + ;; inside an unterminated string started by this char + (condition-case err + (progn (save-excursion (up-list)) nil) + (error + (autopair-in-unterminated-string-p (save-excursion + (goto-char (fourth err)) + (autopair-syntax-ppss))))) + (autopair-in-unterminated-string-p (save-excursion + (goto-char (point-max)) + (autopair-syntax-ppss))) + ;; ... comment-disable or string-disable are true here. + ;; The latter is only useful if we're in a string + ;; terminated by a character other than + ;; `autopair-inserted'. + (some #'(lambda (sym) + (autopair-exception-p where-sym sym autopair-dont-pair)) + '(:comment :string)))) + (setq autopair-action (list 'insert-quote autopair-inserted (point))))) + (autopair-fallback))) + +(put 'autopair-insert-or-skip-quote 'function-documentation + '(concat "Insert or possibly skip over a quoting character.\n\n" + (autopair-document-bindings))) + +(defun autopair-in-unterminated-string-p (autopair-triplet) + (and (eq autopair-inserted (fourth (third autopair-triplet))) + (condition-case nil (progn (scan-sexps (ninth (third autopair-triplet)) 1) nil) (error t)))) + +(defun autopair-insert-opening () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (when (autopair-pair-p) + (setq autopair-action (list 'opening (autopair-find-pair autopair-inserted) (point)))) + (autopair-fallback)) +(put 'autopair-insert-opening 'function-documentation + '(concat "Insert opening delimiter and possibly automatically close it.\n\n" + (autopair-document-bindings))) + +(defun autopair-skip-close-maybe () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (when (autopair-skip-p) + (setq autopair-action (list 'closing (autopair-find-pair autopair-inserted) (point)))) + (autopair-fallback)) +(put 'autopair-skip-close-maybe 'function-documentation + '(concat "Insert or possibly skip over a closing delimiter.\n\n" + (autopair-document-bindings))) + +(defun autopair-backspace () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (when (char-before) + (setq autopair-action (list 'backspace (autopair-find-pair (char-before) 'closing) (point)))) + (autopair-fallback (kbd "DEL"))) +(put 'autopair-backspace 'function-documentation + '(concat "Possibly delete a pair of paired delimiters.\n\n" + (autopair-document-bindings (kbd "DEL")))) + +(defun autopair-newline () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (let ((pair (autopair-find-pair (char-before)))) + (when (and pair + (eq (char-syntax pair) ?\)) + (eq (char-after) pair)) + (setq autopair-action (list 'newline pair (point)))) + (autopair-fallback (kbd "RET")))) +(put 'autopair-newline 'function-documentation + '(concat "Do a smart newline when right between parenthesis.\n +In other words, insert an extra newline along with the one inserted normally +by this command. Then place point after the first, indented.\n\n" + (autopair-document-bindings (kbd "RET")))) + +(defun autopair-skip-p () + (let* ((syntax-triplet (autopair-syntax-ppss)) + (syntax-info (first syntax-triplet)) + (orig-point (point))) + (cond ((eq autopair-skip-criteria 'help-balance) + (save-excursion + (let ((pos-pair (autopair-up-list syntax-info autopair-inserted))) + ;; if `autopair-up-list' returned something valid, we + ;; probably want to skip but only if on of the following is true. + ;; + ;; 1. it returned a cons of equal values (we're not inside any list + ;; + ;; 2. up-listing stopped at a list that contains our original point + ;; + ;; 3. up-listing stopped at a list that does not + ;; contain out original point but its starting + ;; delimiter matches the one we expect. + (and pos-pair + (or (eq (car pos-pair) (cdr pos-pair)) + (< orig-point (cdr pos-pair)) + (eq (char-after (car pos-pair)) + (autopair-find-pair autopair-inserted))))))) + ((eq autopair-skip-criteria 'need-opening) + (save-excursion + (condition-case err + (progn + (backward-list) + t) + (error nil)))) + (t + t)))) + +(defun autopair-pair-p () + (let* ((syntax-triplet (autopair-syntax-ppss)) + (syntax-info (first syntax-triplet)) + (where-sym (second syntax-triplet)) + (orig-point (point))) + (and (not (some #'(lambda (sym) + (autopair-exception-p where-sym sym autopair-dont-pair)) + '(:string :comment :code :everywhere))) + (cond ((eq autopair-pair-criteria 'help-balance) + (and (not (autopair-escaped-p syntax-info)) + (save-excursion + (let ((pos-pair (autopair-up-list syntax-info)) + (prev-point (point-max)) + (expected-closing (autopair-find-pair autopair-inserted))) + (condition-case err + (progn + (while (not (eq prev-point (point))) + (setq prev-point (point)) + (forward-sexp)) + t) + (error + ;; if `forward-sexp' (called byp + ;; `autopair-forward') returned an error. + ;; typically we don't want to autopair, + ;; unless one of the following occurs: + ;; + (cond (;; 1. The error is *not* of type "containing + ;; expression ends prematurely", which means + ;; we're in the "too-many-openings" situation + ;; and thus want to autopair. + (not (string-match "prematurely" (second err))) + t) + (;; 2. We stopped at a closing parenthesis. Do + ;; autopair if we're in a mixed parens situation, + ;; i.e. the last list jumped over was started by + ;; the paren we're trying to match + ;; (`autopair-inserted') and ended by a different + ;; parens, or the closing paren we stopped at is + ;; also different from the expected. The second + ;; `scan-lists' places point at the closing of the + ;; last list we forwarded over. + ;; + (condition-case err + (prog1 + (eq (char-after (scan-lists (point) -1 0)) + autopair-inserted) + (goto-char (scan-lists (point) -1 -1))) + (error t)) + + (or + ;; mixed () ] for input (, yes autopair + (not (eq expected-closing (char-after (third err)))) + ;; mixed (] ) for input (, yes autopair + (not (eq expected-closing (char-after (point)))) + ;; ()) for input (, not mixed + ;; hence no autopair + )) + (t + nil)) + ;; (eq (fourth err) (point-max)) + )))))) + ((eq autopair-pair-criteria 'always) + t) + (t + (not (autopair-escaped-p syntax-info))))))) + +;; post-command-hook stuff +;; +(defun autopair-post-command-handler () + "Performs pairing and wrapping based on `autopair-action' and +`autopair-wrap-action'. " + (when (and autopair-wrap-action + (notany #'null autopair-wrap-action)) + + (if autopair-handle-wrap-action-fns + (condition-case err + (mapc #'(lambda (fn) + (apply fn autopair-wrap-action)) + autopair-handle-wrap-action-fns) + (error (progn + (message "[autopair] error running custom `autopair-handle-wrap-action-fns', switching autopair off") + (autopair-mode -1)))) + (apply #'autopair-default-handle-wrap-action autopair-wrap-action)) + (setq autopair-wrap-action nil)) + + (when (and autopair-action + (notany #'null autopair-action)) + (if autopair-handle-action-fns + (condition-case err + (mapc #'(lambda (fn) + (funcall fn (first autopair-action) (second autopair-action) (third autopair-action))) + autopair-handle-action-fns) + (error (progn + (message "[autopair] error running custom `autopair-handle-action-fns', switching autopair off") + (autopair-mode -1)))) + (apply #'autopair-default-handle-action autopair-action)) + (setq autopair-action nil))) + +(defun autopair-blink-matching-open () + (let ((blink-matching-paren autopair-blink) + (show-paren-mode nil) + (blink-matching-delay autopair-blink-delay)) + (blink-matching-open))) + +(defun autopair-blink (&optional pos) + (when autopair-blink + (if pos + (save-excursion + (goto-char pos) + (sit-for autopair-blink-delay)) + (sit-for autopair-blink-delay)))) + +(defun autopair-default-handle-action (action pair pos-before) + ;;(message "action is %s" action) + (condition-case err + (cond (;; automatically insert closing delimiter + (and (eq 'opening action) + (not (eq pair (char-before)))) + (insert pair) + (autopair-blink) + (backward-char 1)) + (;; automatically insert closing quote delimiter + (eq 'insert-quote action) + (insert pair) + (autopair-blink) + (backward-char 1)) + (;; automatically skip oper closer quote delimiter + (and (eq 'skip-quote action) + (eq pair (char-after (point)))) + (delete-char 1) + (autopair-blink-matching-open)) + (;; skip over newly-inserted-but-existing closing delimiter + ;; (normal case) + (eq 'closing action) + (let ((skipped 0)) + (when autopair-skip-whitespace + (setq skipped (save-excursion (skip-chars-forward "\s\n\t")))) + (when (eq autopair-inserted (char-after (+ (point) skipped))) + (backward-delete-char 1) + (unless (zerop skipped) (autopair-blink (+ (point) skipped))) + (if (eq autopair-skip-whitespace 'chomp) + (delete-char skipped) + (forward-char skipped)) + (forward-char)) + (autopair-blink-matching-open))) + (;; autodelete closing delimiter + (and (eq 'backspace action) + (eq pair (char-after (point)))) + (delete-char 1)) + (;; opens an extra line after point, then indents + (and (eq 'newline action) + (eq pair (char-after (point)))) + (save-excursion + (newline-and-indent)) + (indent-according-to-mode) + (when (or (and (boundp 'global-hl-line-mode) + global-hl-line-mode) + (and (boundp 'hl-line-mode) + hl-line-mode)) + (hl-line-unhighlight) (hl-line-highlight)))) + (error + (message "[autopair] Ignored error in `autopair-default-handle-action'")))) + +(defun autopair-default-handle-wrap-action (action pair pos-before region-before) + "Default handler for the wrapping action in `autopair-wrap'" + (condition-case err + (when (eq 'wrap action) + (let ((delete-active-region nil)) + (cond + ((eq 'opening (first autopair-action)) + (goto-char (1+ (cdr region-before))) + (insert pair) + (autopair-blink) + (goto-char (1+ (car region-before)))) + (;; wraps + (eq 'closing (first autopair-action)) + (delete-backward-char 1) + (insert pair) + (goto-char (1+ (cdr region-before))) + (insert autopair-inserted)) + ((eq 'insert-quote (first autopair-action)) + (goto-char (1+ (cdr region-before))) + (insert pair) + (autopair-blink)) + (t + (delete-backward-char 1) + (goto-char (cdr region-before)) + (insert autopair-inserted))) + (setq autopair-action nil))) + (error + (message "[autopair] Ignored error in `autopair-default-handle-wrap-action'")))) + +;; example python triple quote helper +;; +(defun autopair-python-triple-quote-action (action pair pos-before) + (cond ((and (eq 'insert-quote action) + (>= (point) 3) + (string= (buffer-substring (- (point) 3) + (point)) + (make-string 3 pair))) + (save-excursion (insert (make-string 2 pair)))) + ((and (eq 'backspace action) + (>= (point) 2) + (<= (point) (- (point-max) 2)) + (string= (buffer-substring (- (point) 2) + (+ (point) 2)) + (make-string 4 pair))) + (delete-region (- (point) 2) + (+ (point) 2))) + ((and (eq 'skip-quote action) + (<= (point) (- (point-max) 2)) + (string= (buffer-substring (point) + (+ (point) 2)) + (make-string 2 pair))) + (forward-char 2)) + (t + t))) + +;; example latex paired-delimiter helper +;; +(defun autopair-latex-mode-paired-delimiter-action (action pair pos-before) + "Pair or skip latex's \"paired delimiter\" syntax in math mode. Added AucText support, thanks Massimo Lauria" + (when (eq action 'paired-delimiter) + (when (eq (char-before) pair) + (if (and (or + (eq (get-text-property pos-before 'face) 'tex-math) + (eq (get-text-property (- pos-before 1) 'face) 'font-latex-math-face) + (member 'font-latex-math-face (get-text-property (- pos-before 1) 'face))) + (eq (char-after) pair)) + (cond ((and (eq (char-after) pair) + (eq (char-after (1+ (point))) pair)) + ;; double skip + (delete-char 1) + (forward-char)) + ((eq (char-before pos-before) pair) + ;; doube insert + (insert pair) + (backward-char)) + (t + ;; simple skip + (delete-char 1))) + (insert pair) + (backward-char))))) + +;; Commands and predicates for the autopair-extra* feature +;; + +(defun autopair-extra-insert-opening () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (when (autopair-extra-pair-p) + (setq autopair-action (list 'opening (autopair-find-pair autopair-inserted) (point)))) + (autopair-fallback)) +(put 'autopair-extra-insert-opening 'function-documentation + '(concat "Insert (an extra) opening delimiter and possibly automatically close it.\n\n" + (autopair-document-bindings))) + +(defun autopair-extra-skip-close-maybe () + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (when (autopair-extra-skip-p) + (setq autopair-action (list 'closing autopair-inserted (point)))) + (autopair-fallback)) +(put 'autopair-extra-skip-close-maybe 'function-documentation + '(concat "Insert or possibly skip over a (and extra) closing delimiter.\n\n" + (autopair-document-bindings))) + +(defun autopair-extra-pair-p () + (let* ((syntax-triplet (autopair-syntax-ppss)) + (syntax-info (first syntax-triplet)) + (where-sym (second syntax-triplet))) + (some #'(lambda (sym) + (autopair-exception-p where-sym sym autopair-extra-pairs #'car)) + '(:everywhere :comment :string :code)))) + +(defun autopair-extra-skip-p () + (let* ((syntax-triplet (autopair-syntax-ppss)) + (syntax-info (first syntax-triplet)) + (where-sym (second syntax-triplet)) + (orig-point (point))) + (and (eq (char-after (point)) autopair-inserted) + (some #'(lambda (sym) + (autopair-exception-p where-sym sym autopair-extra-pairs #'cdr)) + '(:comment :string :code :everywhere)) + (save-excursion + (condition-case err + (backward-sexp (point-max)) + (error + (goto-char (third err)))) + (search-forward (make-string 1 (autopair-find-pair autopair-inserted)) + orig-point + 'noerror))))) + +;; Commands and tex-mode specific handler functions for the "paired +;; delimiter" syntax class. +;; +(defun autopair-insert-or-skip-paired-delimiter () + " insert or skip a character paired delimiter" + (interactive) + (setq autopair-inserted (autopair-calculate-inserted)) + (setq autopair-action (list 'paired-delimiter autopair-inserted (point))) + (autopair-fallback)) + +(put 'autopair-insert-or-skip-paired-delimiter 'function-documentation + '(concat "Insert or possibly skip over a character with a syntax-class of \"paired delimiter\"." + (autopair-document-bindings))) + + +;; monkey-patching: Compatibility with delete-selection-mode and cua-mode +;; +;; Ideally one would be able to use functions as the value of the +;; 'delete-selection properties of the autopair commands. The function +;; would return non-nil when no wrapping should/could be performed. +;; +;; Until then use some `defadvice' i.e. monkey-patching, which relies +;; on these features' implementation details. +;; +(put 'autopair-insert-opening 'delete-selection t) +(put 'autopair-skip-close-maybe 'delete-selection t) +(put 'autopair-insert-or-skip-quote 'delete-selection t) +(put 'autopair-extra-insert-opening 'delete-selection t) +(put 'autopair-extra-skip-close-maybe 'delete-selection t) +(put 'autopair-backspace 'delete-selection 'supersede) +(put 'autopair-newline 'delete-selection t) + +(defun autopair-should-autowrap () + (let ((name (symbol-name this-command))) + (and autopair-mode + (not (eq this-command 'autopair-backspace)) + (string-match "^autopair" (symbol-name this-command)) + (autopair-calculate-wrap-action)))) + +(defadvice cua--pre-command-handler-1 (around autopair-override activate) + "Don't actually do anything if autopair is about to autowrap. " + (unless (autopair-should-autowrap) ad-do-it)) + +(defadvice delete-selection-pre-hook (around autopair-override activate) + "Don't actually do anything if autopair is about to autowrap. " + (unless (autopair-should-autowrap) ad-do-it)) + +;;; +(defvar py-menu) +(defvar python-mode-map) +(setq python-mode-map + (let ((map (make-sparse-keymap))) + ;; electric keys + (define-key map [(:)] 'py-electric-colon) + (define-key map [(\#)] 'py-electric-comment) + (define-key map [(delete)] 'py-electric-delete) + (define-key map [(backspace)] 'py-electric-backspace) + (define-key map [(control backspace)] 'py-hungry-delete-backwards) + (define-key map [(control c) (delete)] 'py-hungry-delete-forward) + (define-key map [(control c)(control a)] 'py-mark-statement) + ;; moving point + (define-key map [(control c)(control p)] 'py-beginning-of-statement) + (define-key map [(control c)(control n)] 'py-end-of-statement) + (define-key map [(control c)(control u)] 'py-beginning-of-block) + (define-key map [(control c)(control q)] 'py-end-of-block) + (define-key map [(control meta a)] 'py-beginning-of-def-or-class) + (define-key map [(control meta e)] 'py-end-of-def-or-class) + + ;; (define-key map [(meta i)] 'py-indent-forward-line) + (define-key map [(control j)] 'py-newline-and-indent) + ;; Most Pythoneers expect RET `py-newline-and-indent' + ;; (define-key map (kbd "RET") 'py-newline-and-dedent) + (define-key map (kbd "RET") 'py-newline-and-indent) + ;; (define-key map (kbd "RET") 'newline) + (define-key map [(super backspace)] 'py-dedent) + ;; (define-key map [(control return)] 'py-newline-and-dedent) + ;; indentation level modifiers + (define-key map [(control c)(control l)] 'py-shift-left) + (define-key map [(control c)(control r)] 'py-shift-right) + (define-key map [(control c)(<)] 'py-shift-left) + (define-key map [(control c)(>)] 'py-shift-right) + (define-key map [(control c)(tab)] 'py-indent-region) + (define-key map [(control c)(:)] 'py-guess-indent-offset) + ;; subprocess commands + (define-key map [(control c)(control c)] 'py-execute-buffer) + (define-key map [(control c)(control m)] 'py-execute-import-or-reload) + (define-key map [(control c)(control s)] 'py-execute-string) + (define-key map [(control c)(|)] 'py-execute-region) + (define-key map [(control meta x)] 'py-execute-def-or-class) + (define-key map [(control c)(!)] 'py-shell) + (define-key map [(control c)(control t)] 'py-toggle-shell) + (define-key map [(control meta h)] 'py-mark-def-or-class) + (define-key map [(control c)(control k)] 'py-mark-block-or-clause) + (define-key map [(control c)(.)] 'py-expression) + ;; Miscellaneous + (define-key map [(control c)(control d)] 'py-pdbtrack-toggle-stack-tracking) + (define-key map [(control c)(control f)] 'py-sort-imports) + (define-key map [(control c)(\#)] 'py-comment-region) + (define-key map [(control c)(\?)] 'py-describe-mode) + (define-key map [(control c)(control e)] 'py-describe-symbol) + (define-key map [(control c)(-)] 'py-up-exception) + (define-key map [(control c)(=)] 'py-down-exception) + (define-key map [(control x) (n) (d)] 'py-narrow-to-defun) + ;; information + (define-key map [(control c)(control b)] 'py-submit-bug-report) + (define-key map [(control c)(control v)] 'py-version) + (define-key map [(control c)(control w)] 'py-pychecker-run) + (define-key map (kbd "TAB") 'py-indent-line) + (if py-complete-function + (define-key map [(meta tab)] py-complete-function) + (define-key map [(meta tab)] 'py-shell-complete)) + ;; (substitute-key-definition 'complete-symbol 'completion-at-point + ;; map global-map) + (easy-menu-define py-menu map "Python Tools" + `("PyTools" + :help "Python mode tools" + + ["Customize Python mode" (customize-group 'python-mode) + :help "Open the customization buffer for Python mode"] + + "-" + + ["pychecker-run" py-pychecker-run + :help "`py-pychecker-run' +Run pychecker"] + + ("Pylint ... " + :help "Extendet report options +call `easy_install pylint' if not available" + + ["pylint-run" py-pylint-run + :help "`pylint-run' +Pylint will display a number of messages as it analyzes the code, +as well as some statistics about the number of warnings and +errors found in different files - unless called with arg \"--errors-only\". The messages are classified +under various categories such as errors and warnings + +Pylint checks length of lines of code, if variable names are +well-formed according to your coding standard, if declared +interfaces are truly implemented, and much more. Additionally, it +is possible to write plugins. + +call `easy_install pylint' if not available +"] + + ["pylint-help" pylint-help + :help "`pylint-help' +List extendet report options +"] + ["pylint-flymake-mode" pylint-flymake-mode + :help "`pylint-flymake-mode' +Toggle flymake-mode running `pylint' +"]) + + ("pep8 ... " + :help "Check formatting +call `easy_install pep8' if not available" + + ["pep8-run" py-pep8-run + :help "`py-pep8-run' +Check formatting (default on the file currently visited) +call `easy_install pep8' if not available +"] + + ["pep8-help" py-pep8-help + :help "`py-pep8-help' +Display help for pep8 format checker) +"] + + ["pep8-flymake-mode" pep8-flymake-mode + :help "`pep8-flymake-mode' +Toggle flymake-mode running `pep8' +"]) + + ("Pyflakes ... " :help "Non intrusive code + checker call `easy_install pyflakes' if + not available" + + ["pyflakes-run" py-pyflakes-run :help + "`py-pyflakes-run' Run pyflakes call + `easy_install pyflakes' if not + available"] + + ["pyflakes-help" py-pyflakes-help :help + "`py-pyflakes-help' Display help for + Pyflakes "] + + ["pyflakes-flymake-mode" pyflakes-flymake-mode :help + "`pyflakes-flymake-mode' +Toggle flymake-mode running `pyflakes' "]) + + ("Pyflakes-pep8 ... " :help + "Non intrusive code checker running `pyflakes' and `pep8' +call `easy_install pyflakes' and `easy_install pep8' if basics not available" + + ["pyflakespep8-run" py-pyflakespep8-run :help + "`py-pyflakespep8-run' Run `pyflakespep8' +call `easy_install pyflakes' if not available"] + + ["pyflakespep8-help" py-pyflakespep8-help :help + "`py-pyflakespep8-help' Display help for + Pyflakespep8 "] + + ["pyflakespep8-flymake-mode" pyflakespep8-flymake-mode :help + "`pyflakespep8-flymake-mode' +Toggle flymake-mode running `pyflakespep8' "]) + + "-" + ("Abbrevs" + :help "see also `py-add-abbrev'" + :filter (lambda (&rest junk) + (abbrev-table-menu python-mode-abbrev-table))) + ["add-abbrev" py-add-abbrev + :help "Defines python-mode specific abbrev for last expressions before point. +Argument is how many `py-partial-expression's form the expansion; or zero means the region is the expansion. "] + + ("Skeletons" + :help "See also templates in YASnippet" + + ["if" py-if + :help "Inserts if-statement"] + ["py-else" py-else + :help "Inserts else-statement"] + ["py-while" py-while + :help "Inserts while-statement"] + ["py-for" py-for + :help "Inserts for-statement"] + ["py-try/finally" py-try/finally + :help "Inserts py-try/finally-statement"] + ["py-try/except" py-try/except + :help "Inserts py-try/except-statement"]) + + "-" + + ["Import/reload file" py-execute-import-or-reload + :help "`py-execute-import-or-reload' +Load into inferior Python session"] + + ["Debugger" pdb + :help "`pdb' +Run pdb under GUD"] + "-" + + ["Toggle autopair-mode" autopair-mode + :help "When `py-prepare-autopair-mode-p' is `t', this toggles `autopair-mode' "] + + ["Toggle py-smart-indentation" toggle-py-smart-indentation + :help "See also `py-smart-indentation-on', `-off' "] + + ["Toggle indent-tabs-mode" py-toggle-indent-tabs-mode + :help "See also `py-indent-tabs-mode-on', `-off' "] + + ["Help on symbol" py-describe-symbol + :help "`py-describe-symbol' +Use pydoc on symbol at point"] + ["Complete symbol" py-shell-complete + :help "`py-shell-complete' +Complete (qualified) symbol before point"] + ["Find function" py-find-function + :help "`py-find-function' +Try to find source definition of function at point"] + ["Update imports" py-update-imports + :help "`py-update-imports' +Update list of top-level imports for completion"] + "-" + )) + ;; Menu py-execute forms + (easy-menu-define py-menu map "Execute Python" + `("PyExec" + :help "Python-specific features" + + ["Execute statement" py-execute-statement + :help "`py-execute-statement' + Send statement at point to Python interpreter. "] + + ["Execute block" py-execute-block + :help "`py-execute-block' + Send block at point to Python interpreter. "] + + ["Execute block-or-clause" py-execute-block-or-clause + :help "`py-execute-block-or-clause' + Send block-or-clause at point to Python interpreter. "] + + ["Execute def" py-execute-def + :help "`py-execute-def' + Send def at point to Python interpreter. "] + + ["Execute class" py-execute-class + :help "`py-execute-class' + Send class at point to Python interpreter. "] + + ["Execute region" py-execute-region + :help "`py-execute-region' + Send region at point to Python interpreter. "] + + ["Execute buffer" py-execute-buffer + :help "`py-execute-buffer' + Send buffer at point to Python interpreter. "] + + ["Execute file" py-execute-file + :help "`py-execute-file' + Send file at point to Python interpreter. "] + ["Execute line" py-execute-line + :help "`py-execute-line' + Send current line from beginning of indent to Python interpreter. "] + + ["Execute expression" py-execute-expression + :help "`py-execute-expression' + Send expression at point to Python interpreter. "] + + ["Execute partial-expression" py-execute-partial-expression + :help "`py-execute-partial-expression' + Send partial-expression at point to Python interpreter. "] + + ["Execute line" py-execute-line + :help "`py-execute-line' + Send line at point to Python interpreter. "] + + ;; statement + ("Execute statement ... " + :help "Execute statement functions" + ["py-execute-statement-python" py-execute-statement-python + :help "Execute statement through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-statement-ipython" py-execute-statement-ipython + :help "Execute statement through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-statement-python3" py-execute-statement-python3 + :help "Execute statement through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-statement-python2" py-execute-statement-python2 + :help "Execute statement through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-statement-python2.7" py-execute-statement-python2.7 + :help "Execute statement through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-statement-jython" py-execute-statement-jython + :help "Execute statement through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-statement-python3.2" py-execute-statement-python3.2 + :help "Execute statement through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-statement-python-dedicated" py-execute-statement-python-dedicated + :help "Execute statement through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-ipython-dedicated" py-execute-statement-ipython-dedicated + :help "Execute statement through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python3-dedicated" py-execute-statement-python3-dedicated + :help "Execute statement through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python2-dedicated" py-execute-statement-python2-dedicated + :help "Execute statement through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python2.7-dedicated" py-execute-statement-python2.7-dedicated + :help "Execute statement through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-jython-dedicated" py-execute-statement-jython-dedicated + :help "Execute statement through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python3.2-dedicated" py-execute-statement-python3.2-dedicated + :help "Execute statement through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-statement-python-switch" py-execute-statement-python-switch + :help "Execute statement through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-statement-ipython-switch" py-execute-statement-ipython-switch + :help "Execute statement through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-statement-python3-switch" py-execute-statement-python3-switch + :help "Execute statement through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-statement-python2-switch" py-execute-statement-python2-switch + :help "Execute statement through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-statement-python2.7-switch" py-execute-statement-python2.7-switch + :help "Execute statement through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-statement-jython-switch" py-execute-statement-jython-switch + :help "Execute statement through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-statement-python3.2-switch" py-execute-statement-python3.2-switch + :help "Execute statement through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-statement-python-dedicated-switch" py-execute-statement-python-dedicated-switch + :help "Execute statement through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-ipython-dedicated-switch" py-execute-statement-ipython-dedicated-switch + :help "Execute statement through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python3-dedicated-switch" py-execute-statement-python3-dedicated-switch + :help "Execute statement through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python2-dedicated-switch" py-execute-statement-python2-dedicated-switch + :help "Execute statement through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python2.7-dedicated-switch" py-execute-statement-python2.7-dedicated-switch + :help "Execute statement through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-jython-dedicated-switch" py-execute-statement-jython-dedicated-switch + :help "Execute statement through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-statement-python3.2-dedicated-switch" py-execute-statement-python3.2-dedicated-switch + :help "Execute statement through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; block + ("Execute block ... " + :help "Execute block functions" + ["py-execute-block-python" py-execute-block-python + :help "Execute block through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-block-ipython" py-execute-block-ipython + :help "Execute block through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-block-python3" py-execute-block-python3 + :help "Execute block through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-block-python2" py-execute-block-python2 + :help "Execute block through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-block-python2.7" py-execute-block-python2.7 + :help "Execute block through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-block-jython" py-execute-block-jython + :help "Execute block through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-block-python3.2" py-execute-block-python3.2 + :help "Execute block through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-block-python-dedicated" py-execute-block-python-dedicated + :help "Execute block through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-ipython-dedicated" py-execute-block-ipython-dedicated + :help "Execute block through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python3-dedicated" py-execute-block-python3-dedicated + :help "Execute block through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python2-dedicated" py-execute-block-python2-dedicated + :help "Execute block through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python2.7-dedicated" py-execute-block-python2.7-dedicated + :help "Execute block through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-jython-dedicated" py-execute-block-jython-dedicated + :help "Execute block through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python3.2-dedicated" py-execute-block-python3.2-dedicated + :help "Execute block through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-block-python-switch" py-execute-block-python-switch + :help "Execute block through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-block-ipython-switch" py-execute-block-ipython-switch + :help "Execute block through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-block-python3-switch" py-execute-block-python3-switch + :help "Execute block through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-block-python2-switch" py-execute-block-python2-switch + :help "Execute block through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-block-python2.7-switch" py-execute-block-python2.7-switch + :help "Execute block through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-block-jython-switch" py-execute-block-jython-switch + :help "Execute block through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-block-python3.2-switch" py-execute-block-python3.2-switch + :help "Execute block through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-block-python-dedicated-switch" py-execute-block-python-dedicated-switch + :help "Execute block through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-ipython-dedicated-switch" py-execute-block-ipython-dedicated-switch + :help "Execute block through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python3-dedicated-switch" py-execute-block-python3-dedicated-switch + :help "Execute block through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python2-dedicated-switch" py-execute-block-python2-dedicated-switch + :help "Execute block through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python2.7-dedicated-switch" py-execute-block-python2.7-dedicated-switch + :help "Execute block through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-jython-dedicated-switch" py-execute-block-jython-dedicated-switch + :help "Execute block through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-python3.2-dedicated-switch" py-execute-block-python3.2-dedicated-switch + :help "Execute block through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; block-or-clause + ("Execute block-or-clause ... " + :help "Execute block-or-clause functions" + ["py-execute-block-or-clause-python" py-execute-block-or-clause-python + :help "Execute block-or-clause through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-block-or-clause-ipython" py-execute-block-or-clause-ipython + :help "Execute block-or-clause through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-block-or-clause-python3" py-execute-block-or-clause-python3 + :help "Execute block-or-clause through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-block-or-clause-python2" py-execute-block-or-clause-python2 + :help "Execute block-or-clause through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-block-or-clause-python2.7" py-execute-block-or-clause-python2.7 + :help "Execute block-or-clause through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-block-or-clause-jython" py-execute-block-or-clause-jython + :help "Execute block-or-clause through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-block-or-clause-python3.2" py-execute-block-or-clause-python3.2 + :help "Execute block-or-clause through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-block-or-clause-python-dedicated" py-execute-block-or-clause-python-dedicated + :help "Execute block-or-clause through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-ipython-dedicated" py-execute-block-or-clause-ipython-dedicated + :help "Execute block-or-clause through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python3-dedicated" py-execute-block-or-clause-python3-dedicated + :help "Execute block-or-clause through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python2-dedicated" py-execute-block-or-clause-python2-dedicated + :help "Execute block-or-clause through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python2.7-dedicated" py-execute-block-or-clause-python2.7-dedicated + :help "Execute block-or-clause through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-jython-dedicated" py-execute-block-or-clause-jython-dedicated + :help "Execute block-or-clause through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python3.2-dedicated" py-execute-block-or-clause-python3.2-dedicated + :help "Execute block-or-clause through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-block-or-clause-python-switch" py-execute-block-or-clause-python-switch + :help "Execute block-or-clause through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-block-or-clause-ipython-switch" py-execute-block-or-clause-ipython-switch + :help "Execute block-or-clause through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-block-or-clause-python3-switch" py-execute-block-or-clause-python3-switch + :help "Execute block-or-clause through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-block-or-clause-python2-switch" py-execute-block-or-clause-python2-switch + :help "Execute block-or-clause through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-block-or-clause-python2.7-switch" py-execute-block-or-clause-python2.7-switch + :help "Execute block-or-clause through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-block-or-clause-jython-switch" py-execute-block-or-clause-jython-switch + :help "Execute block-or-clause through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-block-or-clause-python3.2-switch" py-execute-block-or-clause-python3.2-switch + :help "Execute block-or-clause through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-block-or-clause-python-dedicated-switch" py-execute-block-or-clause-python-dedicated-switch + :help "Execute block-or-clause through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-ipython-dedicated-switch" py-execute-block-or-clause-ipython-dedicated-switch + :help "Execute block-or-clause through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python3-dedicated-switch" py-execute-block-or-clause-python3-dedicated-switch + :help "Execute block-or-clause through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python2-dedicated-switch" py-execute-block-or-clause-python2-dedicated-switch + :help "Execute block-or-clause through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python2.7-dedicated-switch" py-execute-block-or-clause-python2.7-dedicated-switch + :help "Execute block-or-clause through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-jython-dedicated-switch" py-execute-block-or-clause-jython-dedicated-switch + :help "Execute block-or-clause through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-block-or-clause-python3.2-dedicated-switch" py-execute-block-or-clause-python3.2-dedicated-switch + :help "Execute block-or-clause through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; def + ("Execute def ... " + :help "Execute def functions" + ["py-execute-def-python" py-execute-def-python + :help "Execute def through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-def-ipython" py-execute-def-ipython + :help "Execute def through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-def-python3" py-execute-def-python3 + :help "Execute def through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-def-python2" py-execute-def-python2 + :help "Execute def through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-def-python2.7" py-execute-def-python2.7 + :help "Execute def through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-def-jython" py-execute-def-jython + :help "Execute def through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-def-python3.2" py-execute-def-python3.2 + :help "Execute def through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-def-python-dedicated" py-execute-def-python-dedicated + :help "Execute def through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-ipython-dedicated" py-execute-def-ipython-dedicated + :help "Execute def through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python3-dedicated" py-execute-def-python3-dedicated + :help "Execute def through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python2-dedicated" py-execute-def-python2-dedicated + :help "Execute def through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python2.7-dedicated" py-execute-def-python2.7-dedicated + :help "Execute def through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-jython-dedicated" py-execute-def-jython-dedicated + :help "Execute def through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python3.2-dedicated" py-execute-def-python3.2-dedicated + :help "Execute def through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-def-python-switch" py-execute-def-python-switch + :help "Execute def through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-def-ipython-switch" py-execute-def-ipython-switch + :help "Execute def through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-def-python3-switch" py-execute-def-python3-switch + :help "Execute def through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-def-python2-switch" py-execute-def-python2-switch + :help "Execute def through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-def-python2.7-switch" py-execute-def-python2.7-switch + :help "Execute def through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-def-jython-switch" py-execute-def-jython-switch + :help "Execute def through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-def-python3.2-switch" py-execute-def-python3.2-switch + :help "Execute def through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-def-python-dedicated-switch" py-execute-def-python-dedicated-switch + :help "Execute def through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-ipython-dedicated-switch" py-execute-def-ipython-dedicated-switch + :help "Execute def through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python3-dedicated-switch" py-execute-def-python3-dedicated-switch + :help "Execute def through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python2-dedicated-switch" py-execute-def-python2-dedicated-switch + :help "Execute def through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python2.7-dedicated-switch" py-execute-def-python2.7-dedicated-switch + :help "Execute def through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-jython-dedicated-switch" py-execute-def-jython-dedicated-switch + :help "Execute def through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-def-python3.2-dedicated-switch" py-execute-def-python3.2-dedicated-switch + :help "Execute def through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; class + ("Execute class ... " + :help "Execute class functions" + ["py-execute-class-python" py-execute-class-python + :help "Execute class through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-class-ipython" py-execute-class-ipython + :help "Execute class through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-class-python3" py-execute-class-python3 + :help "Execute class through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-class-python2" py-execute-class-python2 + :help "Execute class through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-class-python2.7" py-execute-class-python2.7 + :help "Execute class through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-class-jython" py-execute-class-jython + :help "Execute class through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-class-python3.2" py-execute-class-python3.2 + :help "Execute class through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-class-python-dedicated" py-execute-class-python-dedicated + :help "Execute class through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-ipython-dedicated" py-execute-class-ipython-dedicated + :help "Execute class through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python3-dedicated" py-execute-class-python3-dedicated + :help "Execute class through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python2-dedicated" py-execute-class-python2-dedicated + :help "Execute class through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python2.7-dedicated" py-execute-class-python2.7-dedicated + :help "Execute class through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-jython-dedicated" py-execute-class-jython-dedicated + :help "Execute class through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python3.2-dedicated" py-execute-class-python3.2-dedicated + :help "Execute class through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-class-python-switch" py-execute-class-python-switch + :help "Execute class through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-class-ipython-switch" py-execute-class-ipython-switch + :help "Execute class through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-class-python3-switch" py-execute-class-python3-switch + :help "Execute class through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-class-python2-switch" py-execute-class-python2-switch + :help "Execute class through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-class-python2.7-switch" py-execute-class-python2.7-switch + :help "Execute class through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-class-jython-switch" py-execute-class-jython-switch + :help "Execute class through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-class-python3.2-switch" py-execute-class-python3.2-switch + :help "Execute class through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-class-python-dedicated-switch" py-execute-class-python-dedicated-switch + :help "Execute class through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-ipython-dedicated-switch" py-execute-class-ipython-dedicated-switch + :help "Execute class through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python3-dedicated-switch" py-execute-class-python3-dedicated-switch + :help "Execute class through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python2-dedicated-switch" py-execute-class-python2-dedicated-switch + :help "Execute class through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python2.7-dedicated-switch" py-execute-class-python2.7-dedicated-switch + :help "Execute class through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-jython-dedicated-switch" py-execute-class-jython-dedicated-switch + :help "Execute class through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-class-python3.2-dedicated-switch" py-execute-class-python3.2-dedicated-switch + :help "Execute class through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; region + ("Execute region ... " + :help "Execute region functions" + ["py-execute-region-python" py-execute-region-python + :help "Execute region through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-region-ipython" py-execute-region-ipython + :help "Execute region through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-region-python3" py-execute-region-python3 + :help "Execute region through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-region-python2" py-execute-region-python2 + :help "Execute region through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-region-python2.7" py-execute-region-python2.7 + :help "Execute region through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-region-jython" py-execute-region-jython + :help "Execute region through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-region-python3.2" py-execute-region-python3.2 + :help "Execute region through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-region-python-dedicated" py-execute-region-python-dedicated + :help "Execute region through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-ipython-dedicated" py-execute-region-ipython-dedicated + :help "Execute region through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python3-dedicated" py-execute-region-python3-dedicated + :help "Execute region through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python2-dedicated" py-execute-region-python2-dedicated + :help "Execute region through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python2.7-dedicated" py-execute-region-python2.7-dedicated + :help "Execute region through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-jython-dedicated" py-execute-region-jython-dedicated + :help "Execute region through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python3.2-dedicated" py-execute-region-python3.2-dedicated + :help "Execute region through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-region-python-switch" py-execute-region-python-switch + :help "Execute region through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-region-ipython-switch" py-execute-region-ipython-switch + :help "Execute region through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-region-python3-switch" py-execute-region-python3-switch + :help "Execute region through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-region-python2-switch" py-execute-region-python2-switch + :help "Execute region through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-region-python2.7-switch" py-execute-region-python2.7-switch + :help "Execute region through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-region-jython-switch" py-execute-region-jython-switch + :help "Execute region through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-region-python3.2-switch" py-execute-region-python3.2-switch + :help "Execute region through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-region-python-dedicated-switch" py-execute-region-python-dedicated-switch + :help "Execute region through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-ipython-dedicated-switch" py-execute-region-ipython-dedicated-switch + :help "Execute region through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python3-dedicated-switch" py-execute-region-python3-dedicated-switch + :help "Execute region through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python2-dedicated-switch" py-execute-region-python2-dedicated-switch + :help "Execute region through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python2.7-dedicated-switch" py-execute-region-python2.7-dedicated-switch + :help "Execute region through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-jython-dedicated-switch" py-execute-region-jython-dedicated-switch + :help "Execute region through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-region-python3.2-dedicated-switch" py-execute-region-python3.2-dedicated-switch + :help "Execute region through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; buffer + ("Execute buffer ... " + :help "Execute buffer functions" + ["py-execute-buffer-python" py-execute-buffer-python + :help "Execute buffer through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-buffer-ipython" py-execute-buffer-ipython + :help "Execute buffer through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-buffer-python3" py-execute-buffer-python3 + :help "Execute buffer through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-buffer-python2" py-execute-buffer-python2 + :help "Execute buffer through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-buffer-python2.7" py-execute-buffer-python2.7 + :help "Execute buffer through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-buffer-jython" py-execute-buffer-jython + :help "Execute buffer through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-buffer-python3.2" py-execute-buffer-python3.2 + :help "Execute buffer through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-buffer-python-dedicated" py-execute-buffer-python-dedicated + :help "Execute buffer through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-ipython-dedicated" py-execute-buffer-ipython-dedicated + :help "Execute buffer through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python3-dedicated" py-execute-buffer-python3-dedicated + :help "Execute buffer through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python2-dedicated" py-execute-buffer-python2-dedicated + :help "Execute buffer through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python2.7-dedicated" py-execute-buffer-python2.7-dedicated + :help "Execute buffer through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-jython-dedicated" py-execute-buffer-jython-dedicated + :help "Execute buffer through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python3.2-dedicated" py-execute-buffer-python3.2-dedicated + :help "Execute buffer through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-buffer-python-switch" py-execute-buffer-python-switch + :help "Execute buffer through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-buffer-ipython-switch" py-execute-buffer-ipython-switch + :help "Execute buffer through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-buffer-python3-switch" py-execute-buffer-python3-switch + :help "Execute buffer through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-buffer-python2-switch" py-execute-buffer-python2-switch + :help "Execute buffer through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-buffer-python2.7-switch" py-execute-buffer-python2.7-switch + :help "Execute buffer through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-buffer-jython-switch" py-execute-buffer-jython-switch + :help "Execute buffer through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-buffer-python3.2-switch" py-execute-buffer-python3.2-switch + :help "Execute buffer through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-buffer-python-dedicated-switch" py-execute-buffer-python-dedicated-switch + :help "Execute buffer through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-ipython-dedicated-switch" py-execute-buffer-ipython-dedicated-switch + :help "Execute buffer through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python3-dedicated-switch" py-execute-buffer-python3-dedicated-switch + :help "Execute buffer through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python2-dedicated-switch" py-execute-buffer-python2-dedicated-switch + :help "Execute buffer through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python2.7-dedicated-switch" py-execute-buffer-python2.7-dedicated-switch + :help "Execute buffer through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-jython-dedicated-switch" py-execute-buffer-jython-dedicated-switch + :help "Execute buffer through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-buffer-python3.2-dedicated-switch" py-execute-buffer-python3.2-dedicated-switch + :help "Execute buffer through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) + + ;; expression + ("Execute expression ... " + :help "Execute expression functions" + ["py-execute-expression-python" py-execute-expression-python + :help "Execute expression through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-expression-ipython" py-execute-expression-ipython + :help "Execute expression through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-expression-python3" py-execute-expression-python3 + :help "Execute expression through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-expression-python2" py-execute-expression-python2 + :help "Execute expression through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-expression-python2.7" py-execute-expression-python2.7 + :help "Execute expression through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-expression-jython" py-execute-expression-jython + :help "Execute expression through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-expression-python3.2" py-execute-expression-python3.2 + :help "Execute expression through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-expression-python-dedicated" py-execute-expression-python-dedicated + :help "Execute expression through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-ipython-dedicated" py-execute-expression-ipython-dedicated + :help "Execute expression through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python3-dedicated" py-execute-expression-python3-dedicated + :help "Execute expression through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python2-dedicated" py-execute-expression-python2-dedicated + :help "Execute expression through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python2.7-dedicated" py-execute-expression-python2.7-dedicated + :help "Execute expression through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-jython-dedicated" py-execute-expression-jython-dedicated + :help "Execute expression through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python3.2-dedicated" py-execute-expression-python3.2-dedicated + :help "Execute expression through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-expression-python-switch" py-execute-expression-python-switch + :help "Execute expression through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-expression-ipython-switch" py-execute-expression-ipython-switch + :help "Execute expression through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-expression-python3-switch" py-execute-expression-python3-switch + :help "Execute expression through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-expression-python2-switch" py-execute-expression-python2-switch + :help "Execute expression through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-expression-python2.7-switch" py-execute-expression-python2.7-switch + :help "Execute expression through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-expression-jython-switch" py-execute-expression-jython-switch + :help "Execute expression through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-expression-python3.2-switch" py-execute-expression-python3.2-switch + :help "Execute expression through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-expression-python-dedicated-switch" py-execute-expression-python-dedicated-switch + :help "Execute expression through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-ipython-dedicated-switch" py-execute-expression-ipython-dedicated-switch + :help "Execute expression through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python3-dedicated-switch" py-execute-expression-python3-dedicated-switch + :help "Execute expression through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python2-dedicated-switch" py-execute-expression-python2-dedicated-switch + :help "Execute expression through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python2.7-dedicated-switch" py-execute-expression-python2.7-dedicated-switch + :help "Execute expression through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-jython-dedicated-switch" py-execute-expression-jython-dedicated-switch + :help "Execute expression through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-expression-python3.2-dedicated-switch" py-execute-expression-python3.2-dedicated-switch + :help "Execute expression through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) ;; partial-expression + + ("Execute partial-expression ... " + :help "Execute partial-expression functions" + ["py-execute-partial-expression-python" py-execute-partial-expression-python + :help "Execute minor-expression through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-minor-expression-ipython" py-execute-minor-expression-ipython + :help "Execute minor-expression through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-minor-expression-python3" py-execute-minor-expression-python3 + :help "Execute minor-expression through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-minor-expression-python2" py-execute-minor-expression-python2 + :help "Execute minor-expression through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-minor-expression-python2.7" py-execute-minor-expression-python2.7 + :help "Execute minor-expression through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-minor-expression-jython" py-execute-minor-expression-jython + :help "Execute minor-expression through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-minor-expression-python3.2" py-execute-minor-expression-python3.2 + :help "Execute minor-expression through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-minor-expression-python-dedicated" py-execute-minor-expression-python-dedicated + :help "Execute minor-expression through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-ipython-dedicated" py-execute-minor-expression-ipython-dedicated + :help "Execute minor-expression through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python3-dedicated" py-execute-minor-expression-python3-dedicated + :help "Execute minor-expression through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python2-dedicated" py-execute-minor-expression-python2-dedicated + :help "Execute minor-expression through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python2.7-dedicated" py-execute-minor-expression-python2.7-dedicated + :help "Execute minor-expression through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-jython-dedicated" py-execute-minor-expression-jython-dedicated + :help "Execute minor-expression through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python3.2-dedicated" py-execute-minor-expression-python3.2-dedicated + :help "Execute minor-expression through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-minor-expression-python-switch" py-execute-minor-expression-python-switch + :help "Execute minor-expression through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-minor-expression-ipython-switch" py-execute-minor-expression-ipython-switch + :help "Execute minor-expression through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-minor-expression-python3-switch" py-execute-minor-expression-python3-switch + :help "Execute minor-expression through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-minor-expression-python2-switch" py-execute-minor-expression-python2-switch + :help "Execute minor-expression through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-minor-expression-python2.7-switch" py-execute-minor-expression-python2.7-switch + :help "Execute minor-expression through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-minor-expression-jython-switch" py-execute-minor-expression-jython-switch + :help "Execute minor-expression through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-minor-expression-python3.2-switch" py-execute-minor-expression-python3.2-switch + :help "Execute minor-expression through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-minor-expression-python-dedicated-switch" py-execute-minor-expression-python-dedicated-switch + :help "Execute minor-expression through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-ipython-dedicated-switch" py-execute-minor-expression-ipython-dedicated-switch + :help "Execute minor-expression through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python3-dedicated-switch" py-execute-minor-expression-python3-dedicated-switch + :help "Execute minor-expression through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python2-dedicated-switch" py-execute-minor-expression-python2-dedicated-switch + :help "Execute minor-expression through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python2.7-dedicated-switch" py-execute-minor-expression-python2.7-dedicated-switch + :help "Execute minor-expression through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-jython-dedicated-switch" py-execute-minor-expression-jython-dedicated-switch + :help "Execute minor-expression through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-minor-expression-python3.2-dedicated-switch" py-execute-minor-expression-python3.2-dedicated-switch + :help "Execute minor-expression through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ) ;; line + + ("Execute line ... " + :help "Execute line functions" + ["py-execute-line-python" py-execute-line-python + :help "Execute line through a Python interpreter. + With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-line-ipython" py-execute-line-ipython + :help "Execute line through an IPython interpreter. + With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-line-python3" py-execute-line-python3 + :help "Execute line through a Python3 interpreter. + With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-line-python2" py-execute-line-python2 + :help "Execute line through a Python2 interpreter. + With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-line-python2.7" py-execute-line-python2.7 + :help "Execute line through a Python2.7 interpreter. + With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-line-jython" py-execute-line-jython + :help "Execute line through a Jython interpreter. + With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-line-python3.2" py-execute-line-python3.2 + :help "Execute line through a Python3.2 interpreter. + With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated + ["py-execute-line-python-dedicated" py-execute-line-python-dedicated + :help "Execute line through a unique Python interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-ipython-dedicated" py-execute-line-ipython-dedicated + :help "Execute line through a unique IPython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python3-dedicated" py-execute-line-python3-dedicated + :help "Execute line through a unique Python3 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python2-dedicated" py-execute-line-python2-dedicated + :help "Execute line through a unique Python2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python2.7-dedicated" py-execute-line-python2.7-dedicated + :help "Execute line through a unique Python2.7 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-jython-dedicated" py-execute-line-jython-dedicated + :help "Execute line through a unique Jython interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python3.2-dedicated" py-execute-line-python3.2-dedicated + :help "Execute line through a unique Python3.2 interpreter. +Optional \\[universal-argument] forces switch to output buffer, ignores `py-switch-buffers-on-execute-p'. "] + ;; switch + ["py-execute-line-python-switch" py-execute-line-python-switch + :help "Execute line through a Python interpreter. +With \\[universal-argument] use an unique Python interpreter. "] + ["py-execute-line-ipython-switch" py-execute-line-ipython-switch + :help "Execute line through an IPython interpreter. +With \\[universal-argument] use an unique IPython interpreter. "] + ["py-execute-line-python3-switch" py-execute-line-python3-switch + :help "Execute line through a Python3 interpreter. +With \\[universal-argument] use an unique Python3 interpreter. "] + ["py-execute-line-python2-switch" py-execute-line-python2-switch + :help "Execute line through a Python2 interpreter. +With \\[universal-argument] use an unique Python2 interpreter. "] + ["py-execute-line-python2.7-switch" py-execute-line-python2.7-switch + :help "Execute line through a Python2.7 interpreter. +With \\[universal-argument] use an unique Python2.7 interpreter. "] + ["py-execute-line-jython-switch" py-execute-line-jython-switch + :help "Execute line through a Jython interpreter. +With \\[universal-argument] use an unique Jython interpreter. "] + ["py-execute-line-python3.2-switch" py-execute-line-python3.2-switch + :help "Execute line through a Python3.2 interpreter. +With \\[universal-argument] use an unique Python3.2 interpreter. "] + ;; dedicated-switch + ["py-execute-line-python-dedicated-switch" py-execute-line-python-dedicated-switch + :help "Execute line through a unique Python interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-ipython-dedicated-switch" py-execute-line-ipython-dedicated-switch + :help "Execute line through a uniquen IPython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python3-dedicated-switch" py-execute-line-python3-dedicated-switch + :help "Execute line through a unique Python3 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python2-dedicated-switch" py-execute-line-python2-dedicated-switch + :help "Execute line through a unique Python2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python2.7-dedicated-switch" py-execute-line-python2.7-dedicated-switch + :help "Execute line through a unique Python2.7 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-jython-dedicated-switch" py-execute-line-jython-dedicated-switch + :help "Execute line through a unique Jython interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ["py-execute-line-python3.2-dedicated-switch" py-execute-line-python3.2-dedicated-switch + :help "Execute line through a unique Python3.2 interpreter. +Switch to output buffer; ignores `py-switch-buffers-on-execute-p'. "] + ))) + + ;; Menu command forms + (easy-menu-define py-menu map "Python Mode Commands" + `("PyEdit" + :help "Python-specific features" + ["Copy block" py-copy-block + :help "`py-copy-block' +Copy innermost compound statement at point"] + + ["Copy clause" py-copy-clause + :help "`py-copy-clause' +Copy clause at point"] + + ["Copy def-or-class" py-copy-def-or-class + :help "`py-copy-def-or-class' +Copy innermost definition at point"] + + ["Copy def" py-copy-def + :help "`py-copy-def' +Copy method/function definition at point"] + + ["Copy class" py-copy-class + :help "`py-copy-class' +Copy class definition at point"] + + ["Copy statement" py-copy-statement + :help "`py-copy-statement' +Copy statement at point"] + ["Copy expression" py-copy-expression + :help "`py-copy-expression' +Copy expression at point"] + + ["Copy partial expression" py-copy-partial-expression + :help "`py-copy-partial-expression' +\".\" operators delimit a partial-expression expression on it's level"] + "-" + ["Beginning of block" py-beginning-of-block + :help "`py-beginning-of-block' +Go to start of innermost compound statement at point"] + ["End of block" py-end-of-block + :help "`py-end-of-block' +Go to end of innermost compound statement at point"] + ["Beginning of Def-or-Class" py-beginning-of-def-or-class + :help "`py-beginning-of-def-or-class' +Go to start of innermost definition at point"] + ["End of Def-or-Class" py-end-of-def-or-class + :help "`py-end-of-def-or-class' +Go to end of innermost function definition at point"] + ["Beginning of class" py-beginning-of-class + :help "`py-beginning-of-class' +Go to start of class definition "] + ["End of class" py-end-of-class + :help "`py-end-of-class' +Go to end of class definition "] + ["Beginning of statement" py-beginning-of-statement + :help "`py-beginning-of-statement' +Go to start of a Python statement"] + ["End of statement" py-end-of-statement + :help "`py-end-of-statement' +Go to end of a Python statement"] + ["Beginning of expression" py-beginning-of-expression + :help "Go to the beginning of a compound python expression. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + ["End of expression" py-end-of-expression + :help "`py-end-of-expression' +Go to the end of a compound python expression. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + ["Beginning of minor expression" py-beginning-of-partial-expression + :help "`py-beginning-of-partial-expression' +Go to start of an minor expression + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + ["End of partial-expression" py-end-of-partial-expression + :help "`py-end-of-partial-expression' +Go to end of an partial-expression + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + ["Backward into nomenclature" py-backward-into-nomenclature + :help " `py-backward-into-nomenclature' +Go backward into nomenclature + +A nomenclature is a fancy way of saying AWordWithMixedCaseNotUnderscores. "] + ["Forward into nomenclature" py-forward-into-nomenclature + :help " `py-forward-into-nomenclature' +Go forward into nomenclature + +A nomenclature is a fancy way of saying AWordWithMixedCaseNotUnderscores. "] + "-" + ["Down statement lc" py-down-statement-lc + :help "`py-down-statement-lc' +Goto beginning of line following end of statement. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-statement' stops at right corner. + +See also `py-down-statement': down from current definition to next beginning of statement below. "] + ["Down block lc" py-down-block-lc + :help "`py-down-block-lc' +Goto beginning of line following end of block. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-block' stops at right corner. + +See also `py-down-block': down from current definition to next beginning of block below. "] + ["Down def lc" py-down-def-lc + :help "`py-down-def-lc' +Goto beginning of line following end of def. + +Returns position reached, if successful, nil otherwise. + +\"-lc\" stands for \"left-corner\" - a complementary command travelling left, whilst `py-end-of-def' stops at right corner. + +See also `py-down-def': down from current definition to next beginning of def below. + "] + ["Down statement" py-down-statement + :help "`py-down-statement' + +Go to the beginning of next statement below in buffer. + +Returns indentation if statement found, nil otherwise. "] + ["Down block" py-down-block + :help "`py-down-block' + +Go to the beginning of next block below in buffer. + +Returns indentation if block found, nil otherwise. "] + ["Down def" py-down-def + :help "`py-down-def' + +Go to the beginning of next function definition below in buffer. + +Returns indentation if found, nil otherwise. "])) + ;; Python shell menu + (easy-menu-define py-menu map "Python Shells" + `("PyShell" + :help "Python Shells" + ["Default interpreter" py-shell + :help "`py-shell' +Switch to `inferior' Python in separate buffer"] + + ;; ["Toggle enforcement of default interpreter" toggle-force-py-shell-name-p + ;; :help "If customized default `py-shell-name' should be enforced upon execution. "] + + ["Enforce py-shell-name" force-py-shell-name-p-on + :help "Enforce customized default `py-shell-name' should upon execution. "] + + ["Don't enforce default interpreter" force-py-shell-name-p-off + :help "Make execute commands guess interpreter from environment"] + + ;; ["Enforce locally Python shell sessions interpreter " toggle-force-local-shell + ;; :help "If locally indicated Python shell should be taken and + ;; enforced upon sessions execute commands. "] + + ["Enforce local Python shell " py-force-local-shell-on + :help "Locally indicated Python being enforced upon sessions execute commands. "] + + ["Remove local Python shell enforcement, restore default" py-force-local-shell-off + :help "Restore `py-shell-name' default value and `behaviour'. "] + + "-" + + ["python" python + :help "`python' +Start an Python interpreter. + +Optional C-u prompts for options to pass to the Python interpreter. See `py-python-command-args'."] + ["ipython" ipython + :help "`ipython' +Start an IPython interpreter. + +Optional C-u prompts for options to pass to the IPython interpreter. See `py-python-command-args'."] + ["python3" python3 + :help "`python3' +Start an Python3 interpreter. + +Optional C-u prompts for options to pass to the Python3 interpreter. See `py-python-command-args'."] + ["python2" python2 + :help "`python2' +Start an Python2 interpreter. + +Optional C-u prompts for options to pass to the Python2 interpreter. See `py-python-command-args'."] + ["python2.7" python2.7 + :help "`python2.7' +Start an Python2.7 interpreter. + +Optional C-u prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'."] + ["jython" jython + :help "`jython' +Start an Jython interpreter. + +Optional C-u prompts for options to pass to the Jython interpreter. See `py-python-command-args'."] + ["python3.2" python3.2 + :help "`python3.2' +Start an Python3.2 interpreter. + +Optional C-u prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'."] + "-" + ["python-dedicated" python-dedicated + :help "`python-dedicated' +Start an unique Python interpreter in another window. + +Optional C-u prompts for options to pass to the Python interpreter. See `py-python-command-args'."] + ["ipython-dedicated" ipython-dedicated + :help "`ipython-dedicated' +Start an unique IPython interpreter in another window. + +Optional C-u prompts for options to pass to the IPython interpreter. See `py-python-command-args'."] + ["python3-dedicated" python3-dedicated + :help "`python3-dedicated' +Start an unique Python3 interpreter in another window. + +Optional C-u prompts for options to pass to the Python3 interpreter. See `py-python-command-args'."] + ["python2-dedicated" python2-dedicated + :help "`python2-dedicated' +Start an unique Python2 interpreter in another window. + +Optional C-u prompts for options to pass to the Python2 interpreter. See `py-python-command-args'."] + ["python2.7-dedicated" python2.7-dedicated + :help "`python2'.7-dedicated +Start an unique Python2.7 interpreter in another window. + +Optional C-u prompts for options to pass to the Python2.7 interpreter. See `py-python-command-args'."] + ["jython-dedicated" jython-dedicated + :help "`jython-dedicated' +Start an unique Jython interpreter in another window. + +Optional C-u prompts for options to pass to the Jython interpreter. See `py-python-command-args'."] + ["python3.2-dedicated" python3.2-dedicated + :help "`python3.2-dedicated' +Start an unique Python3.2 interpreter in another window. + +Optional C-u prompts for options to pass to the Python3.2 interpreter. See `py-python-command-args'."] + "-" + + ["Toggle split-windows-on-execute" py-toggle-split-windows-on-execute + :help "Switch boolean `py-split-windows-on-execute-p'."] + ["Switch split-windows-on-execute ON" py-split-windows-on-execute-on + :help "Switch `py-split-windows-on-execute-p' ON. "] + ["Switch split-windows-on-execute OFF" py-split-windows-on-execute-off + :help "Switch `py-split-windows-on-execute-p' OFF. "] + + ["Toggle shell-switch-buffers-on-execute" py-toggle-shell-switch-buffers-on-execute + :help "Switch boolean `py-switch-buffers-on-execute-p'."] + ["Switch shell-switch-buffers-on-execute ON" py-shell-switch-buffers-on-execute-on + :help "Switch `py-switch-buffers-on-execute-p' ON. "] + ["Switch shell-switch-buffers-on-execute OFF" py-shell-switch-buffers-on-execute-off + :help "Switch `py-switch-buffers-on-execute-p' OFF. "])) + map)) + +;;; Abbrevs +(defun py-edit-abbrevs () + "Jumps to `python-mode-abbrev-table' in a buffer containing lists of abbrev definitions. +You can edit them and type \\<edit-abbrevs-map>\\[edit-abbrevs-redefine] to redefine abbrevs +according to your editing. +Buffer contains a header line for each abbrev table, + which is the abbrev table name in parentheses. +This is followed by one line per abbrev in that table: +NAME USECOUNT EXPANSION HOOK +where NAME and EXPANSION are strings with quotes, +USECOUNT is an integer, and HOOK is any valid function +or may be omitted (it is usually omitted). " + (interactive) + (save-excursion + (let ((mat (abbrev-table-name local-abbrev-table))) + (prepare-abbrev-list-buffer) + (set-buffer "*Abbrevs*") + (switch-to-buffer (current-buffer)) + (goto-char (point-min)) + (search-forward (concat "(" (format "%s" mat)))))) + +(defun py-add-abbrev-propose (table type arg &optional dont-ask) + (save-excursion + (let ((orig (point)) + proposal exp) + (if (= arg 0) + (setq exp + (buffer-substring-no-properties + (point) + (mark))) + (while (< 0 arg) + (py-beginning-of-partial-expression) + (when (looking-at "[[:alpha:]]") + (setq proposal (concat (downcase (match-string-no-properties 0)) proposal))) + (setq arg (1- arg))) + (setq exp (buffer-substring-no-properties (point) orig))) + (setq name + ;; ask only when interactive + (if dont-ask + proposal + (read-string (format (if exp "%s abbrev for \"%s\": " + "Undefine %s abbrev: ") + type exp) proposal))) + (set-text-properties 0 (length name) nil name) + (when (or (null exp) + (not (abbrev-expansion name table)) + (y-or-n-p (format "%s expands to \"%s\"; redefine? " + name (abbrev-expansion name table)))) + (define-abbrev table (downcase name) exp))))) + +(defun py-add-abbrev (arg) + "Defines python-mode specific abbrev for last expressions before point. +Argument is how many `py-partial-expression's form the expansion; or zero means the region is the expansion. + +Reads the abbreviation in the minibuffer; with numeric arg it displays a proposal for an abbrev. +Proposal is composed from the initial character(s) of the +expansion. + +Don't use this function in a Lisp program; use `define-abbrev' instead." + (interactive "p") + (save-excursion + (py-add-abbrev-propose + (if only-global-abbrevs + global-abbrev-table + (or local-abbrev-table + (error "No per-mode abbrev table"))) + "Mode" arg))) + +;;; Hooks +;; arrange to kill temp files when Emacs exists +(add-hook 'kill-emacs-hook 'py-kill-emacs-hook) +(add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file) + +(add-hook 'inferior-python-mode-hook 'py-send-shell-setup-code) + +(remove-hook 'python-mode-hook 'python-setup-brm) +(add-hook 'python-mode-hook + #'(lambda () + (when py-smart-indentation + (if (bobp) + (save-excursion + (save-restriction + (widen) + (while (and (not (eobp)) + (or + (let ((erg (syntax-ppss))) + (or (nth 1 erg) (nth 8 erg))) + (eq 0 (current-indentation)))) + (forward-line 1)) + (back-to-indentation) + (py-guess-indent-offset))) + (py-guess-indent-offset))))) + +(add-hook 'which-func-functions 'python-which-func nil t) + +(add-hook 'comint-output-filter-functions + 'py-comint-output-filter-function) + +(add-hook 'python-mode-hook + (lambda () + (setq indent-tabs-mode py-indent-tabs-mode) + (set (make-local-variable 'beginning-of-defun-function) 'py-beginning-of-def-or-class) + (set (make-local-variable 'end-of-defun-function) 'py-end-of-def-or-class) + ;; (orgstruct-mode 1) + )) +(when py-warn-tmp-files-left-p + (add-hook 'python-mode-hook 'py-warn-tmp-files-left)) + +;; FixMe: for unknown reasons this is not done by mode +(if (file-readable-p abbrev-file-name) + (add-hook 'python-mode-hook '(lambda () (load abbrev-file-name nil t))) + (message "Warning: %s" "no abbrev-file found, customize `abbrev-file-name' in order to make mode-specific abbrevs work. ")) + +;;; + +(add-to-list 'hs-special-modes-alist + (list + 'python-mode + ;; start regex + (concat (if py-hide-show-hide-docstrings + "^\\s-*\"\"\"\\|" "") + (mapconcat 'identity + (mapcar #'(lambda (x) (concat "^\\s-*" x "\\_>")) + py-hide-show-keywords) + "\\|")) + ;; end regex + nil + ;; comment-start regex + "#" + ;; forward-sexp function + (lambda (arg) + (py-down-block-lc) + (skip-chars-backward " \t\n")) + nil)) + +(setq imenu-generic-expression 'py-imenu-generic-regexp) +;;; +(defvar skeleton-further-elements) +(define-derived-mode python-mode fundamental-mode python-mode-modeline-display + "Major mode for editing Python files. + +To submit a problem report, enter `\\[py-submit-bug-report]' from a +`python-mode' buffer. Do `\\[py-describe-mode]' for detailed +documentation. To see what version of `python-mode' you are running, +enter `\\[py-version]'. + +This mode knows about Python indentation, tokens, comments and +continuation lines. Paragraphs are separated by blank lines only. + +COMMANDS + +VARIABLES + +py-indent-offset\t\tindentation increment +py-block-comment-prefix\t\tcomment string used by `comment-region' +py-shell-name\t\tshell command to invoke Python interpreter +py-temp-directory\t\tdirectory used for temp files (if needed) +py-beep-if-tab-change\t\tring the bell if `tab-width' is changed + +\\{python-mode-map}" + :group 'python-mode +;;; Local vars + (set (make-local-variable 'outline-regexp) + (concat (mapconcat 'identity + (mapcar #'(lambda (x) (concat "^\\s-*" x "\\_>")) + py-outline-mode-keywords) + "\\|"))) + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil + (font-lock-syntactic-keywords + . python-font-lock-syntactic-keywords) + ;; This probably isn't worth it. + ;; (font-lock-syntactic-face-function + ;; . python-font-lock-syntactic-face-function) + )) + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-start-skip) "^[ \t]*#+ *") + (set (make-local-variable 'comment-column) 40) + (set (make-local-variable 'comment-indent-function) #'py-comment-indent-function) + (set (make-local-variable 'indent-region-function) 'py-indent-region) + (set (make-local-variable 'indent-line-function) 'py-indent-line) + (set (make-local-variable 'hs-hide-comments-when-hiding-all) 'py-hide-comments-when-hiding-all) + (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) + (set (make-local-variable 'add-log-current-defun-function) 'py-current-defun) + (set (make-local-variable 'paragraph-start) "\\s-*$") + (set (make-local-variable 'fill-paragraph-function) 'py-fill-paragraph) + (set (make-local-variable 'require-final-newline) mode-require-final-newline) + (make-local-variable 'python-saved-check-command) + (set (make-local-variable 'tab-width) py-indent-offset) + (set (make-local-variable 'eldoc-documentation-function) + #'py-eldoc-function) + (set (make-local-variable 'skeleton-further-elements) + '((< '(backward-delete-char-untabify (min py-indent-offset + (current-column)))) + (^ '(- (1+ (current-indentation)))))) + + (when py-prepare-autopair-mode-p + (add-hook 'python-mode-hook + #'(lambda () + (setq autopair-handle-action-fns + (list #'autopair-default-handle-action + #'autopair-python-triple-quote-action))))) + + (when (and py-imenu-create-index-p (fboundp 'imenu-add-to-menubar)(ignore-errors (require 'imenu))) + (setq imenu-create-index-function #'py-imenu-create-index-new) + (imenu-add-to-menubar "PyIndex")) + ;; (when py-imenu-create-index-p (imenu-add-to-menubar "PyIndex")) + + ;; Now guess `py-indent-offset' + + ;; add the menu + (if py-menu + (easy-menu-add py-menu)) + (when py-hide-show-minor-mode-p (hs-minor-mode 1)) + ;; (py-send-string "import emacs") + + (when py-start-run-py-shell + ;; py-shell may split window, provide restore + (window-configuration-to-register 213465879) + (unless (get-process (py-process-name)) + (let ((oldbuf (current-buffer))) + (save-excursion + (py-shell) + (set-buffer oldbuf)))) + (jump-to-register 213465879)) + ;; (run-mode-hooks 'python-mode-hook) + (when py-outline-minor-mode-p (outline-minor-mode 1)) + (when (interactive-p) (message "python-mode loaded from: %s" "python-mode.el"))) + +(defadvice pdb (before gud-query-cmdline activate) + "Provide a better default command line when called interactively." + (interactive + (list (gud-query-cmdline pdb-path + (file-name-nondirectory buffer-file-name))))) + +(defalias 'py-hungry-delete-forward 'c-hungry-delete-forward) +(defalias 'py-hungry-delete-backwards 'c-hungry-delete-backwards) + +(define-derived-mode python2-mode python-mode "Python2" + "Edit and run code used by Python version 2 series. " + :group 'Python + :abbrev nil + (set (make-local-variable 'py-exec-command) '(format "execfile(r'%s') # PYTHON-MODE\n" filename)) + (set (make-local-variable 'py-exec-string-command) '(format "exec(r'%s') # PYTHON-MODE\n" string)) + (py-toggle-shell "python2")) + +(define-derived-mode python3-mode python-mode "Python3" + "Edit and run code used by Python version 3 series. " + :group 'Python + :abbrev nil + (set (make-local-variable 'py-exec-command) '(format "exec(compile(open('%s').read(), '%s', 'exec')) # PYTHON-MODE\n" file file)) + (set (make-local-variable 'py-exec-string-command) '(format "exec(r'(%s)') # PYTHON-MODE\n" string)) + (py-toggle-shell "python3")) + +;; Utilities + +(defun py-def-or-class-beginning-position () + "Returns beginning position of function or class definition. " + (interactive) + (let ((here (point)) + (pos (progn (py-beginning-of-def-or-class)(point)))) + (prog1 + (point) + (when (and py-verbose-p (interactive-p)) (message "%s" pos)) + (goto-char here)))) + +(defun py-def-or-class-end-position () + "Returns end position of function or class definition. " + (interactive) + (let ((here (point)) + (pos (progn (py-end-of-def-or-class) (point)))) + (prog1 + (point) + (when (and py-verbose-p (interactive-p)) (message "%s" pos)) + (goto-char here)))) + +(defun py-statement-beginning-position () + "Returns beginning position of statement. " + (interactive) + (let ((here (point)) + (pos (progn (py-beginning-of-statement)(point)))) + (prog1 + (point) + (when (and py-verbose-p (interactive-p)) (message "%s" pos)) + (goto-char here)))) + +(defun py-statement-end-position () + "Returns end position of statement. " + (interactive) + (let (erg) + (save-excursion + (setq erg (py-end-of-statement))) + (when (and py-verbose-p (interactive-p)) (message "%s" erg)) + erg)) + +(defun py-current-indentation () + "Returns beginning position of code in line. " + (interactive) + (let ((here (point)) + (pos (progn (back-to-indentation)(point)))) + (prog1 + (point) + (when (and py-verbose-p (interactive-p)) (message "%s" pos)) + (goto-char here)))) + +(make-obsolete 'jpython-mode 'jython-mode nil) +(define-derived-mode jython-mode python-mode "Jython" + "Major mode for editing Jython files. +Like `python-mode', but sets up parameters for Jython subprocesses. +Runs `jython-mode-hook' after `python-mode-hook'." + :group 'python-mode + (py-toggle-shell "jython")) + +;; It's handy to add recognition of Python files to the +;; interpreter-mode-alist and to auto-mode-alist. With the former, we +;; can specify different `derived-modes' based on the #! line, but +;; with the latter, we can't. So we just won't add them if they're +;; already added. + +(let ((modes '(("jython" . jython-mode) + ("python" . python-mode) + ("python3" . python-mode)))) + (while modes + (when (not (assoc (car modes) interpreter-mode-alist)) + (push (car modes) interpreter-mode-alist)) + (setq modes (cdr modes)))) + +(when (not (or (rassq 'python-mode auto-mode-alist) + (rassq 'jython-mode auto-mode-alist))) + (push '("\\.py$" . python-mode) auto-mode-alist)) + +(defun py-kill-emacs-hook () + "Delete files in `py-file-queue'. +These are Python temporary files awaiting execution." + (mapc #'(lambda (filename) + (ignore-errors (delete-file filename))) + py-file-queue)) + +;; arrange to kill temp files when Emacs exists +(add-hook 'kill-emacs-hook 'py-kill-emacs-hook) +(add-hook 'comint-output-filter-functions 'py-pdbtrack-track-stack-file) + +;; inside python-mode already +;; (add-hook 'python-mode-hook +;; (lambda () +;; (defvar py-mode-map python-mode-map)) +;; (set (make-local-variable 'beginning-o1f-defun-function) 'py-beginning-of-def-or-class) +;; (set (make-local-variable 'end-of-defun-function) 'py-end-of-def-or-class)) + +;; Add a designator to the minor mode strings +(or (assq 'py-pdbtrack-is-tracking-p minor-mode-alist) + (push '(py-pdbtrack-is-tracking-p py-pdbtrack-minor-mode-string) + minor-mode-alist)) + +(defun py-python-version (&optional executable verbose) + "Returns versions number of a Python EXECUTABLE, string. + +If no EXECUTABLE given, `py-shell-name' is used. +Interactively output of `--version' is displayed. " + (interactive) + (let* ((executable (or executable py-shell-name)) + (erg (string-strip (shell-command-to-string (concat executable " --version"))))) + (when (interactive-p) (message "%s" erg)) + (unless verbose (setq erg (cadr (split-string erg)))) + erg)) + +(defun py-version () + "Echo the current version of `python-mode' in the minibuffer." + (interactive) + (message "Using `python-mode' version %s" py-version) + (py-keep-region-active)) + +(defun py-install-search-local () + (interactive) + (let ((erg (split-string (shell-command-to-string (concat "find " default-directory " -maxdepth 9 -type f -name \"*python\""))))))) + +;; (defun py-install-local-epdfree () +;; (interactive) +;; (py-install-local-shells "MY-PATH/epdfree")) + +(defun py-install-local-shells (&optional local path-prefix) + "Builds Python-shell commands from executable found in LOCAL. + +If LOCAL is empty, shell-command `find' searches beneath current directory. +Eval resulting buffer to install it, see customizable `py-extensions'. " + (interactive) + (let* ((local-dir (if local + (expand-file-name local) + (read-from-minibuffer "Virtualenv directory: " default-directory))) + (path-separator (if (string-match "/" local-dir) + "/" + "\\" t)) + (shells (split-string (shell-command-to-string (concat "find " local-dir " -maxdepth 9 -type f -executable -name \"*python\"")))) + erg newshell prefix akt end orig) + (set-buffer (get-buffer-create py-extensions)) + (erase-buffer) + (switch-to-buffer (current-buffer)) + (dolist (elt shells) + (setq prefix "") + (setq curexe (substring elt (1+ (string-match "/[^/]+$" elt)))) + (setq aktpath (substring elt 0 (1+ (string-match "/[^/]+$" elt)))) + (dolist (prf (split-string aktpath (regexp-quote path-separator))) + (unless (string= "" prf) + (setq prefix (concat prefix (substring prf 0 1))))) + (setq orig (point)) + (insert py-shell-template) + (setq end (point)) + (goto-char orig) + (when (re-search-forward "\\<NAME\\>" end t 1) + (replace-match (concat prefix "-" (substring elt (1+ (save-match-data (string-match "/[^/]+$" elt)))))t)) + (goto-char orig) + (while (search-forward "DOCNAME" end t 1) + (replace-match (if (string= "ipython" curexe) + "IPython" + (capitalize curexe)) t)) + (goto-char orig) + (when (search-forward "FULLNAME" end t 1) + (replace-match elt t)) + (goto-char (point-max))) + (emacs-lisp-mode) + (if (file-readable-p (concat py-install-directory "/" py-extensions)) + (find-file (concat py-install-directory "/" py-extensions))))) + +;;; Utility stuff + +;; for toggling between CPython and JPython +(defvar python-which-shell nil) +(defvar python-which-args python-python-command-args) +(defvar python-which-bufname "Python") +(make-variable-buffer-local 'python-which-shell) +(make-variable-buffer-local 'python-which-args) +(make-variable-buffer-local 'python-which-bufname) + +;; Add a designator to the minor mode strings +(or (assq 'python-pdbtrack-is-tracking-p minor-mode-alist) + (push '(python-pdbtrack-is-tracking-p python-pdbtrack-minor-mode-string) + minor-mode-alist)) + +;; Bind python-file-queue before installing the kill-emacs-hook. +(defvar python-file-queue nil + "Queue of Python temp files awaiting execution. +Currently-active file is at the head of the list.") + +(defvar python-pdbtrack-is-tracking-p nil) + +(defconst python-pdbtrack-stack-entry-regexp + "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "Regular expression pdbtrack uses to find a stack trace entry.") + +(defconst python-pdbtrack-input-prompt "\n[(<]*[Ii]?[Pp]db[>)]+ " + "Regular expression pdbtrack uses to recognize a pdb prompt.") + +(defconst python-pdbtrack-track-range 10000 + "Max number of characters from end of buffer to search for stack entry.") + +;;;; Inferior mode stuff (following cmuscheme). + +(defconst python-compilation-regexp-alist + ;; FIXME: maybe these should move to compilation-error-regexp-alist-alist. + ;; The first already is (for CAML), but the second isn't. Anyhow, + ;; these are specific to the inferior buffer. -- fx + `((,(rx line-start (1+ (any " \t")) "File \"" + (group (1+ (not (any "\"<")))) ; avoid `<stdin>' &c + "\", line " (group (1+ digit))) + 1 2) + (,(rx " in file " (group (1+ not-newline)) " on line " + (group (1+ digit))) + 1 2) + ;; pdb stack trace + (,(rx line-start "> " (group (1+ (not (any "(\"<")))) + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + 1 2)) + "`compilation-error-regexp-alist' for inferior Python.") + +(defvar inferior-python-mode-syntax-table + (let ((st (make-syntax-table py-mode-syntax-table))) + ;; Don't get confused by apostrophes in the process's output (e.g. if + ;; you execute "help(os)"). + (modify-syntax-entry ?\' "." st) + ;; Maybe we should do the same for double quotes? + ;; (modify-syntax-entry ?\" "." st) + st)) + +;; Autoloaded. +(declare-function compilation-shell-minor-mode "compile" (&optional arg)) + +;; Fixme: This should inherit some stuff from `python-mode', but I'm +;; not sure how much: at least some keybindings, like C-c C-f; +;; syntax?; font-locking, e.g. for triple-quoted strings? +(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 \\[py-shell]. + +Hooks `comint-mode-hook' and `inferior-python-mode-hook' are run in +that order. + +You can send text to the inferior Python process from other buffers +containing Python source. + * \\[python-switch-to-python] switches the current buffer to the Python + process buffer. + * \\[python-send-region] sends the current region to the Python process. + * \\[python-send-region-and-go] switches to the Python process buffer + after sending the text. +For running multiple processes in multiple buffers, see `run-python' and +`python-buffer'. + +\\{inferior-python-mode-map}" + :group 'python-mode + (setq mode-line-process '(":%s"))) + +(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 py-send-region (start end) + "Send the region to the inferior Python process." + ;; The region is evaluated from a temporary file. This avoids + ;; problems with blank lines, which have different semantics + ;; interactively and in files. It also saves the inferior process + ;; buffer filling up with interpreter prompts. We need a Python + ;; function to remove the temporary file when it has been evaluated + ;; (though we could probably do it in Lisp with a Comint output + ;; filter). This function also catches exceptions and truncates + ;; tracebacks not to mention the frame of the function itself. + ;; + ;; The `compilation-shell-minor-mode' parsing takes care of relating + ;; the reference to the temporary file to the source. + ;; + ;; Fixme: Write a `coding' header to the temp file if the region is + ;; non-ASCII. + (interactive "r") + (let* ((f (make-temp-file "py")) + (command + ;; IPython puts the FakeModule module into __main__ so + ;; emacs.eexecfile becomes useless. + (if (string-match "^ipython" py-shell-name) + (format "execfile %S" f) + (format "emacs.eexecfile(%S)" f))) + (orig-start (copy-marker start))) + (when (save-excursion + (goto-char start) + (/= 0 (current-indentation))) ; need dummy block + (save-excursion + (goto-char orig-start) + ;; Wrong if we had indented code at buffer start. + (set-marker orig-start (line-beginning-position 0))) + (write-region "if True:\n" nil f nil 'nomsg)) + (write-region start end f t 'nomsg) + (python-send-command command) + (with-current-buffer (process-buffer (py-proc)) + ;; Tell compile.el to redirect error locations in file `f' to + ;; positions past marker `orig-start'. It has to be done *after* + ;; `python-send-command''s call to `compilation-forget-errors'. + (compilation-fake-loc orig-start f)))) + +(defun py-send-buffer () + "Send the current buffer to the inferior Python process." + (interactive) + (py-send-region (point-min) (point-max))) + +(defun py-switch-to-python (eob-p) + "Switch to the Python process buffer, maybe starting new process. + +With prefix arg, position cursor at end of buffer." + (interactive "P") + (pop-to-buffer (process-buffer (py-proc)) t) ;Runs python if needed. + (when eob-p + (push-mark) + (goto-char (point-max)))) + +(defun py-send-region-and-go (start end) + "Send the region to the inferior Python process. + +Then switch to the process buffer." + (interactive "r") + (py-send-region start end) + (py-switch-to-python t)) + +(defvar python-prev-dir/file nil + "Caches (directory . file) pair used in the last `py-load-file' command. +Used for determining the default in the next one.") + +(defun py-load-file (file-name) + "Load a Python file FILE-NAME into the inferior Python process. + +If the file has extension `.py' import or reload it as a module. +Treating it as a module keeps the global namespace clean, provides +function location information for debugging, and supports users of +module-qualified names." + (interactive (comint-get-source "Load Python file: " python-prev-dir/file + python-source-modes + t)) ; because execfile needs exact name + (comint-check-source file-name) ; Check to see if buffer needs saving. + (setq python-prev-dir/file (cons (file-name-directory file-name) + (file-name-nondirectory file-name))) + (with-current-buffer (process-buffer (py-proc)) ;Runs python if needed. + ;; Fixme: I'm not convinced by this logic from python-mode.el. + (python-send-command + (if (string-match "\\.py\\'" file-name) + (let ((module (file-name-sans-extension + (file-name-nondirectory file-name)))) + (format "emacs.eimport(%S,%S)" + module (file-name-directory file-name))) + (format "execfile(%S)" file-name))) + (message "%s loaded" file-name))) + +;;; Python-el completion and help + +(defvar view-return-to-alist) +(defvar python-imports) ; forward declaration + +;; Called from `python-mode', this causes a recursive call of the +;; mode. See logic there to break out of the recursion. + +;; pdb tracking is alert once this file is loaded, but takes no action if +;; `python-pdbtrack-do-tracking-p' is nil. +(add-hook 'comint-output-filter-functions 'python-pdbtrack-track-stack-file) + + + +(defun python-comint-output-filter-function (string) + "Watch output for Python prompt and exec next file waiting in queue. +This function is appropriate for `comint-output-filter-functions'." + ;; TBD: this should probably use split-string + (when (and (string-match python--prompt-regexp string) + python-file-queue) + (condition-case nil + (delete-file (car python-file-queue)) + (error nil)) + (setq python-file-queue (cdr python-file-queue)) + (if python-file-queue + (let ((pyproc (get-buffer-process (current-buffer)))) + (python-execute-file pyproc (car python-file-queue)))))) + +(defun python-pdbtrack-overlay-arrow (activation) + "Activate or deactivate arrow at beginning-of-line in current buffer." + (if activation + (progn + (setq overlay-arrow-position (make-marker) + overlay-arrow-string "=>" + python-pdbtrack-is-tracking-p t) + (set-marker overlay-arrow-position + (save-excursion (beginning-of-line) (point)) + (current-buffer))) + (setq overlay-arrow-position nil + python-pdbtrack-is-tracking-p nil))) + +(defun python-pdbtrack-track-stack-file (text) + "Show the file indicated by the pdb stack entry line, in a separate window. + +Activity is disabled if the buffer-local variable +`python-pdbtrack-do-tracking-p' is nil. + +We depend on the pdb input prompt being a match for +`python-pdbtrack-input-prompt'. + +If the traceback target file path is invalid, we look for the +most recently visited python-mode buffer which either has the +name of the current function or class, or which defines the +function or class. This is to provide for scripts not in the +local filesytem (e.g., Zope's 'Script \(Python)', but it's not +Zope specific). If you put a copy of the script in a buffer +named for the script and activate python-mode, then pdbtrack will +find it." + ;; Instead of trying to piece things together from partial text + ;; (which can be almost useless depending on Emacs version), we + ;; monitor to the point where we have the next pdb prompt, and then + ;; check all text from comint-last-input-end to process-mark. + ;; + ;; Also, we're very conservative about clearing the overlay arrow, + ;; to minimize residue. This means, for instance, that executing + ;; other pdb commands wipe out the highlight. You can always do a + ;; 'where' (aka 'w') PDB command to reveal the overlay arrow. + + (let* ((origbuf (current-buffer)) + (currproc (get-buffer-process origbuf))) + + (if (not (and currproc python-pdbtrack-do-tracking-p)) + (python-pdbtrack-overlay-arrow nil) + + (let* ((procmark (process-mark currproc)) + (block (buffer-substring (max comint-last-input-end + (- procmark + python-pdbtrack-track-range)) + procmark)) + target target_fname target_lineno target_buffer) + + (if (not (string-match (concat python-pdbtrack-input-prompt "$") block)) + (python-pdbtrack-overlay-arrow nil) + + (setq target (python-pdbtrack-get-source-buffer block)) + + (if (stringp target) + (progn + (python-pdbtrack-overlay-arrow nil) + (message "pdbtrack: %s" target)) + + (setq target_lineno (car target) + target_buffer (cadr target) + target_fname (buffer-file-name target_buffer)) + (switch-to-buffer-other-window target_buffer) + (goto-char (point-min)) + (forward-line (1- target_lineno)) + (message "pdbtrack: line %s, file %s" target_lineno target_fname) + (python-pdbtrack-overlay-arrow t) + (pop-to-buffer origbuf t) + ;; in large shell buffers, above stuff may cause point to lag output + (goto-char procmark))))))) + +(defun python-pdbtrack-get-source-buffer (block) + "Return line number and buffer of code indicated by block's traceback text. + +We look first to visit the file indicated in the trace. + +Failing that, we look for the most recently visited python-mode buffer +with the same name or having the named function. + +If we're unable find the source code we return a string describing the +problem." + + (if (not (string-match python-pdbtrack-stack-entry-regexp block)) + + "Traceback cue not found" + + (let* ((filename (match-string 1 block)) + (lineno (string-to-number (match-string 2 block))) + (funcname (match-string 3 block)) + funcbuffer) + + (cond ((file-exists-p filename) + (list lineno (find-file-noselect filename))) + + ((setq funcbuffer (python-pdbtrack-grub-for-buffer funcname lineno)) + (if (string-match "/Script (Python)$" filename) + ;; Add in number of lines for leading '##' comments: + (setq lineno + (+ lineno + (with-current-buffer funcbuffer + (if (equal (point-min)(point-max)) + 0 + (count-lines + (point-min) + (max (point-min) + (string-match "^\\([^#]\\|#[^#]\\|#$\\)" + (buffer-substring + (point-min) (point-max)))))))))) + (list lineno funcbuffer)) + + ((= (elt filename 0) ?\<) + (format "(Non-file source: '%s')" filename)) + + (t (format "Not found: %s(), %s" funcname filename)))))) + +(defun python-pdbtrack-grub-for-buffer (funcname lineno) + "Find recent python-mode buffer named, or having function named funcname." + (let ((buffers (buffer-list)) + buf + got) + (while (and buffers (not got)) + (setq buf (car buffers) + buffers (cdr buffers)) + (if (and (with-current-buffer buf + (string= major-mode "python-mode")) + (or (string-match funcname (buffer-name buf)) + (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+" + funcname "\\s-*(") + (with-current-buffer buf + (buffer-substring (point-min) + (point-max)))))) + (setq got buf))) + got)) + +;; Python subprocess utilities and filters +(defun python-execute-file (proc filename) + "Send to Python interpreter process PROC \"execfile('FILENAME')\". +Make that process's buffer visible and force display. Also make +comint believe the user typed this string so that +`kill-output-from-shell' does The Right Thing." + (let ((curbuf (current-buffer)) + (procbuf (process-buffer proc)) + ; (comint-scroll-to-bottom-on-output t) + (msg (format "## working on region in file %s...\n" filename)) + ;; add some comment, so that we can filter it out of history + (cmd (format "execfile(r'%s') # PYTHON-MODE\n" filename))) + (unwind-protect + (with-current-buffer procbuf + (goto-char (point-max)) + (move-marker (process-mark proc) (point)) + (funcall (process-filter proc) proc msg)) + (set-buffer curbuf)) + (process-send-string proc cmd))) + +;; from pycomplete.el +(defun py-find-global-imports () + (save-excursion + (let (first-class-or-def imports) + (goto-char (point-min)) + (setq first-class-or-def + (re-search-forward "^ *\\(def\\|class\\) " nil t)) + (goto-char (point-min)) + (while (re-search-forward + "^\\(import \\|from \\([A-Za-z_][A-Za-z_0-9]*\\) import \\).*" + nil t) + (setq imports (append imports + (list (buffer-substring + (match-beginning 0) + (match-end 0)))))) + imports))) + +;;; Code Completion. + +;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2008-01/msg00076.html +(defalias + 'py-shell-redirect-send-command-to-process + 'comint-redirect-send-command-to-process) +(defalias + 'py-shell-dynamic-simple-complete + 'comint-dynamic-simple-complete) + +(defvar python-imports "None" + "String of top-level import statements updated by `py-find-imports'.") +(make-variable-buffer-local 'python-imports) + +;;; Python Shell Complete +;; Author: Lukasz Pankowski +(defun py-shell-simple-send (proc string) + (comint-simple-send proc string)) + +(defun py-shell-execute-string-now (string &optional shell buffer) + "Send to Python interpreter process PROC \"exec STRING in {}\". +and return collected output" + (let* ((procbuf (or buffer (py-shell nil nil shell))) + + (proc (get-buffer-process procbuf)) + (cmd (format "exec '''%s''' in {}" + (mapconcat 'identity (split-string string "\n") "\\n"))) + (outbuf (get-buffer-create " *pyshellcomplete-output*")) + ;; (lines (reverse py-shell-input-lines)) + ) + ;; (when proc + (unwind-protect + (condition-case nil + (progn + ;; (if lines + ;; (with-current-buffer procbuf + ;; (comint-redirect-send-command-to-process + ;; "\C-c" outbuf proc nil t) + ;; ;; wait for output + ;; (while (not comint-redirect-completed) + ;; (accept-process-output proc 1)))) + (with-current-buffer outbuf + (delete-region (point-min) (point-max))) + (with-current-buffer procbuf + (comint-redirect-send-command-to-process + cmd outbuf proc nil t) + (while (not comint-redirect-completed) ; wait for output + (accept-process-output proc 1))) + (with-current-buffer outbuf + (buffer-substring (point-min) (point-max)))) + (quit (with-current-buffer procbuf + (interrupt-process proc comint-ptyp) + (while (not comint-redirect-completed) ; wait for output + (accept-process-output proc 1))) + (signal 'quit nil))) + ;; (if (with-current-buffer procbuf comint-redirect-completed) + ;; (while lines + ;; (with-current-buffer procbuf + ;; (comint-redirect-send-command-to-process + ;; (car lines) outbuf proc nil t)) + ;; (accept-process-output proc 1) + ;; (setq lines (cdr lines)))) + ))) + +(defun py-dot-word-before-point () + (buffer-substring + (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point)) + (point))) + +(defun py-completion-at-point () + "An alternative completion, similar the way python.el does it. " + (interactive "*") + (let* ((start (when (skip-chars-backward "[[:alnum:]_]")(point))) + (end (progn (skip-chars-forward "[[:alnum:]_]")(point))) + (completion (when start + (py-symbol-completions (buffer-substring-no-properties start end))))) + (if completion + (progn + (delete-region start end) + (insert (car completion))) + (tab-to-tab-stop)))) + +;; started from python.el's python-completion-at-point +(defun py-script-complete () + (interactive "*") + (let ((end (point)) + (start (save-excursion + (and (re-search-backward + (rx (or buffer-start (regexp "[^[:alnum:]._]")) + (group (1+ (regexp "[[:alnum:]._]"))) point) + nil t) + (match-beginning 1))))) + (when start + (list start end + (completion-table-dynamic 'py-symbol-completions))))) + +(defun py-symbol-completions (symbol) + "Return a list of completions of the string SYMBOL from Python process. +The list is sorted. +Uses `python-imports' to load modules against which to complete." + (when (stringp symbol) + (let ((completions + (condition-case () + (car (read-from-string + (python-send-receive + (format "emacs.complete(%S,%s)" + (substring-no-properties symbol) + python-imports)))) + (error nil)))) + (sort + ;; We can get duplicates from the above -- don't know why. + (delete-dups completions) + #'string<)))) + +(defun py-python-script-complete (&optional shell imports beg end word) + "Complete word before point, if any. Otherwise insert TAB. " + (interactive) + (let* (py-split-windows-on-execute-p + py-switch-buffers-on-execute-p + (orig (point)) + (shell (or shell py-local-versioned-command (py-choose-shell))) + (beg (or beg (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point)))) + (end (or end (point))) + (word (or word (buffer-substring-no-properties beg end))) + (imports (or imports (py-find-imports))) + ) + (cond ((string= word "") + (message "%s" "Nothing to complete. ") + (tab-to-tab-stop)) + (t (or (setq proc (get-buffer-process (py-buffer-name-prepare shell))) + (setq proc (get-buffer-process (py-shell nil nil shell)))) + (if (processp proc) + (progn + ;; when completing instances, make them known + (when (string-match "^\\(^[a-zA-Z0-9_]+\\)\\.\\([a-zA-Z0-9_]+\\)$" word) + ;; (message "%s" (match-string 1 word)) + (save-excursion + (save-match-data + (goto-char (point-min)) + (when (re-search-forward (concat "^[ \t]*" (match-string-no-properties 1 word) "[ \t]*=[ \t]*[^ \n\r\f\t]+") nil t 1))) + (if imports + (unless (string-match (concat "import " (match-string-no-properties 1 word) ";") imports) + (setq imports + (concat imports (concat "import" (match-string-no-properties 1 word) ";")))) + (setq imports (match-string-no-properties 0 word))))) + (python-shell-completion--do-completion-at-point proc imports word) + ;; (unless (python-shell-completion--do-completion-at-point proc imports word) + (when (eq (point) orig) + (if (and (not (window-full-height-p)) + (buffer-live-p (get-buffer "*Python Completions*"))) + (progn + (set-buffer "*Python Completions*") + (switch-to-buffer (current-buffer)) + (delete-other-windows) + (search-forward word)) + (dabbrev-expand nil))) + nil) + (error "No completion process at proc")))))) + +(defun py-python2-shell-complete (&optional shell) + (interactive) + (let* (py-split-windows-on-execute-p + py-switch-buffers-on-execute-p + (shell (or shell py-local-versioned-command)) + (orig (point)) + (beg (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point))) + (end (point)) + (word (buffer-substring-no-properties beg end))) + (cond ((string= word "") + (message "%s" "Nothing to complete. ") + (tab-to-tab-stop)) + (t (or (setq proc (get-buffer-process shell)) + (setq proc (get-buffer-process (py-shell nil nil shell)))) + (message "%s" (processp proc)) + (python-shell-completion--do-completion-at-point proc nil word)))) + nil) + +(defun py-python3-shell-complete (&optional shell) + "Complete word before point, if any. Otherwise insert TAB. " + (interactive) + (let* ((shell (or shell py-local-versioned-command)) + (orig (point)) + (beg (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point))) + (end (point)) + (word (buffer-substring-no-properties beg end))) + (cond ((string= word "") + (message "%s" "Nothing to complete. ") + (tab-to-tab-stop)) + (t + (python-shell-completion--do-completion-at-point (get-buffer-process (current-buffer)) nil word) + nil)))) + +(defun py-shell-complete (&optional shell) + "Complete word before point, if any. Otherwise insert TAB. " + (interactive) + ;; (window-configuration-to-register 313465889) + ;; (save-window-excursion + (if (or (eq major-mode 'comint-mode)(eq major-mode 'inferior-python-mode)) + ;; kind of completion resp. to shell + (let (py-fontify-shell-buffer-p + (shell (or shell (py-report-executable (buffer-name (current-buffer)))))) + (if (string-match "[iI][pP]ython" shell) + (ipython-complete) + (let* ((orig (point)) + (beg (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point))) + (end (point)) + (word (buffer-substring-no-properties beg end))) + (cond ((string= word "") + (tab-to-tab-stop)) + ((string-match "[pP]ython3[^[:alpha:]]*$" shell) + (python-shell-completion--do-completion-at-point (get-buffer-process (current-buffer)) "" word)) + (t (py-shell-complete-intern word beg end shell)))))) + ;; complete in script buffer + (let* ((shell (or shell (py-choose-shell))) + py-split-windows-on-execute-p + py-switch-buffers-on-execute-p + (proc (or (get-buffer-process shell) + (get-buffer-process (py-shell nil nil shell 'noswitch nil)))) + (beg (save-excursion (skip-chars-backward "a-zA-Z0-9_.") (point))) + (end (point)) + (word (buffer-substring-no-properties beg end)) + (imports (py-find-imports))) + (cond ((string= word "") + (tab-to-tab-stop)) + ((string-match "[iI][pP]ython" shell) + (ipython-complete)) + ((string-match "[pP]ython3[^[:alpha:]]*$" shell) + (python-shell-completion--do-completion-at-point proc (buffer-substring-no-properties beg end) word)) + (imports + (py-python-script-complete shell imports beg end word)) + (t (py-shell-complete-intern word beg end shell imports)))))) + +(defun py-shell-complete-intern (word &optional beg end shell imports) + (let (result) + (if imports + (setq result (py-shell-execute-string-now (format (concat imports " +def print_completions(namespace, text, prefix=''): + for name in namespace: + if name.startswith(text): + print(prefix + name) + +def complete(text): + import __builtin__ + import __main__ + if '.' in text: + terms = text.split('.') + try: + if hasattr(__main__, terms[0]): + obj = getattr(__main__, terms[0]) + else: + obj = getattr(__builtin__, terms[0]) + for term in terms[1:-1]: + obj = getattr(obj, term) + print_completions(dir(obj), terms[-1], text[:text.rfind('.') + 1]) + except AttributeError: + pass + else: + import keyword + print_completions(keyword.kwlist, text) + print_completions(dir(__builtin__), text) + print_completions(dir(__main__), text) +complete('%s')") word) shell)) + (setq result (py-shell-execute-string-now (format " +def print_completions(namespace, text, prefix=''): + for name in namespace: + if name.startswith(text): + print(prefix + name) + +def complete(text): + import __builtin__ + import __main__ + if '.' in text: + terms = text.split('.') + try: + if hasattr(__main__, terms[0]): + obj = getattr(__main__, terms[0]) + else: + obj = getattr(__builtin__, terms[0]) + for term in terms[1:-1]: + obj = getattr(obj, term) + print_completions(dir(obj), terms[-1], text[:text.rfind('.') + 1]) + except AttributeError: + pass + else: + import keyword + print_completions(keyword.kwlist, text) + print_completions(dir(__builtin__), text) + print_completions(dir(__main__), text) +complete('%s')" word) shell (when (comint-check-proc (current-buffer)) (current-buffer))))) + (if (or (eq result nil)(string= "" result)) + (message "Can't complete") + (setq result (replace-regexp-in-string comint-prompt-regexp "" result)) + (let ((comint-completion-addsuffix nil) + (completions + (sort + (delete-dups (if (split-string "\n" "\n") + (split-string result "\n" t) ; XEmacs + (split-string result "\n"))) + #'string<))) + (if (string= (car completions) word) + (tab-to-tab-stop) + (delete-region beg end) + (insert (car completions)))) + ;; list-typ return required by `completion-at-point' + (point)))) + +;;; IPython Shell Complete + +;; see also +;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2008-01/msg00076.html + +(defun ipython-complete (&optional done completion-command-string beg end word) + "Complete the python symbol before point. + +If no completion available, insert a TAB. +Returns the completed symbol, a string, if successful, nil otherwise. " + + (interactive "*") + (let* (py-split-windows-on-execute-p + py-switch-buffers-on-execute-p + (beg (or beg (progn (save-excursion (skip-chars-backward "a-z0-9A-Z_." (point-at-bol)) + (point))))) + (end (or end (point))) + (pattern (or word (buffer-substring-no-properties beg end))) + (sep ";") + (pyshellname "ipython") + (processlist (process-list)) + done + (process + (if ipython-complete-use-separate-shell-p + (unless (and (buffer-live-p "*IPython-Complete*") + (comint-check-proc (process-name (get-buffer-process "*IPython-Complete*")))) + (get-buffer-process (py-shell nil nil pyshellname 'noswitch nil "*IPython-Complete*"))) + (progn + (while (and processlist (not done)) + (when (and + (string= pyshellname (process-name (car processlist))) + (processp (car processlist)) + (setq done (car processlist)))) + (setq processlist (cdr processlist))) + done))) + (python-process (or process + (get-buffer-process (py-shell nil nil (if (string-match "[iI][pP]ython[^[:alpha:]]*$" pyshellname) pyshellname "ipython") 'noswitch nil)))) + (comint-output-filter-functions + (delq 'py-comint-output-filter-function comint-output-filter-functions)) + (comint-output-filter-functions + (append comint-output-filter-functions + '(ansi-color-filter-apply + (lambda (string) + (setq ugly-return (concat ugly-return string)) + (delete-region comint-last-output-start + (process-mark (get-buffer-process (current-buffer)))))))) + + (ccs (or completion-command-string (py-set-ipython-completion-command-string + (process-name python-process)))) + completion completions completion-table ugly-return) + (if (string= pattern "") + (tab-to-tab-stop) + (process-send-string python-process (format ccs pattern)) + (accept-process-output python-process 5) + (setq completions + (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) + (tab-to-tab-stop)) + ((null completion) + ;; if an (I)Python shell didn't run + ;; before, first completion are not delivered + ;; (if done (ipython-complete done) + (message "Can't find completion for \"%s\"" pattern) + (ding) + nil) + ((not (string= pattern completion)) + (delete-region beg end) + (insert completion) + nil) + (t + (when py-verbose-p (message "Making completion list...")) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list (all-completions pattern completion-table))) + nil))))) + +(defun ipython-complete-py-shell-name (&optional done) + "Complete the python symbol before point. + +If no completion available, insert a TAB. +Returns the completed symbol, a string, if successful, nil otherwise. + +Bug: if no IPython-shell is running, fails first time due to header returned, which messes up the result. Please repeat once then. " + (interactive "*") + (let* (py-split-windows-on-execute-p + py-switch-buffers-on-execute-p + (beg (progn (save-excursion (skip-chars-backward "a-z0-9A-Z_." (point-at-bol)) + (point)))) + (end (point)) + (pattern (buffer-substring-no-properties beg end)) + (sep ";") + (py-process (or (get-buffer-process (current-buffer)) + (get-buffer-process (py-shell)) + (get-buffer-process (py-shell nil nil "ipython" 'noswitch nil)))) + + (comint-output-filter-functions + (delq 'py-comint-output-filter-function comint-output-filter-functions)) + (comint-output-filter-functions + (append comint-output-filter-functions + '(ansi-color-filter-apply + (lambda (string) + (setq ugly-return (concat ugly-return string)) + (delete-region comint-last-output-start + (process-mark (get-buffer-process (current-buffer)))))))) + completion completions completion-table ugly-return) + (if (string= pattern "") + (tab-to-tab-stop) + (process-send-string py-process + (format (py-set-ipython-completion-command-string (downcase (process-name py-process))) pattern)) + (accept-process-output py-process) + (setq completions + (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) + ;; if an (I)Python shell didn't run + ;; before, first completion are not delivered + ;; (if done (ipython-complete done) + (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")))) + completion)) + +;;; pep8 +(defun py-pep8-run (command) + "*Run pep8, check formatting (default on the file currently visited). +" + (interactive + (let ((default + (if (buffer-file-name) + (format "%s %s %s" py-pep8-command + (mapconcat 'identity py-pep8-command-args " ") + (buffer-file-name)) + (format "%s %s" py-pep8-command + (mapconcat 'identity py-pep8-command-args " ")))) + (last (when py-pep8-history + (let* ((lastcmd (car py-pep8-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (buffer-file-name) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pep8 like this: " + (if last + last + default) + 'py-pep8-history) + (read-string "Run pep8 like this: " + (if last + last + default) + 'py-pep8-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defun py-pep8-help () + "Display pep8 command line help messages. " + (interactive) + (set-buffer (get-buffer-create "*pep8-Help*")) + (erase-buffer) + (shell-command "pep8 --help" "*pep8-Help*")) + +;;; Pylint +(defalias 'pylint 'py-pylint-run) +(defun py-pylint-run (command) + "*Run pylint (default on the file currently visited). + +For help see M-x pylint-help resp. M-x pylint-long-help. +Home-page: http://www.logilab.org/project/pylint " + (interactive + (let ((default + (if (buffer-file-name) + (format "%s %s %s" py-pylint-command + (mapconcat 'identity py-pylint-command-args " ") + (buffer-file-name)) + (format "%s %s" py-pylint-command + (mapconcat 'identity py-pylint-command-args " ")))) + (last (when py-pylint-history + (let* ((lastcmd (car py-pylint-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (buffer-file-name) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pylint like this: " + (if last + last + default) + 'py-pylint-history) + (read-string "Run pylint like this: " + (if last + last + default) + 'py-pylint-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defalias 'pylint-help 'py-pylint-help) +(defun py-pylint-help () + "Display Pylint command line help messages. + +Let's have this until more Emacs-like help is prepared " + (interactive) + (set-buffer (get-buffer-create "*Pylint-Help*")) + (erase-buffer) + (shell-command "pylint --long-help" "*Pylint-Help*")) + +(defalias 'pylint-doku 'py-pylint-doku) +(defun py-pylint-doku () + "Display Pylint Documentation. + +Calls `pylint --full-documentation'" + (interactive) + (set-buffer (get-buffer-create "*Pylint-Documentation*")) + (erase-buffer) + (shell-command "pylint --full-documentation" "*Pylint-Documentation*")) + +;;; Pyflakes +(defalias 'pyflakes 'py-pyflakes-run) +(defun py-pyflakes-run (command) + "*Run pyflakes (default on the file currently visited). + +For help see M-x pyflakes-help resp. M-x pyflakes-long-help. +Home-page: http://www.logilab.org/project/pyflakes " + (interactive + (let ((default + (if (buffer-file-name) + (format "%s %s %s" py-pyflakes-command + (mapconcat 'identity py-pyflakes-command-args " ") + (buffer-file-name)) + (format "%s %s" py-pyflakes-command + (mapconcat 'identity py-pyflakes-command-args " ")))) + (last (when py-pyflakes-history + (let* ((lastcmd (car py-pyflakes-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (buffer-file-name) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pyflakes like this: " + (if last + last + default) + 'py-pyflakes-history) + (read-string "Run pyflakes like this: " + (if last + last + default) + 'py-pyflakes-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defalias 'pyflakes-help 'py-pyflakes-help) +(defun py-pyflakes-help () + "Display Pyflakes command line help messages. + +Let's have this until more Emacs-like help is prepared " + (interactive) + ;; (set-buffer (get-buffer-create "*Pyflakes-Help*")) + ;; (erase-buffer) + (with-help-window "*Pyflakes-Help*" + (with-current-buffer standard-output + (insert " pyflakes [file-or-directory ...] + + Pyflakes is a simple program which checks Python + source files for errors. It is similar to + PyChecker in scope, but differs in that it does + not execute the modules to check them. This is + both safer and faster, although it does not + perform as many checks. Unlike PyLint, Pyflakes + checks only for logical errors in programs; it + does not perform any checks on style. + + All commandline arguments are checked, which + have to be either regular files or directories. + If a directory is given, every .py file within + will be checked. + + When no commandline arguments are given, data + will be read from standard input. + + The exit status is 0 when no warnings or errors + are found. When errors are found the exit status + is 2. When warnings (but no errors) are found + the exit status is 1. + +Extracted from http://manpages.ubuntu.com/manpages/natty/man1/pyflakes.1.html +")))) + +;;; Pyflakes-pep8 +(defalias 'pyflakespep8 'py-pyflakespep8-run) +(defun py-pyflakespep8-run (command) + "*Run pyflakespep8, check formatting (default on the file currently visited). +" + (interactive + (let ((default + (if (buffer-file-name) + (format "%s %s %s" py-pyflakespep8-command + (mapconcat 'identity py-pyflakespep8-command-args " ") + (buffer-file-name)) + (format "%s %s" py-pyflakespep8-command + (mapconcat 'identity py-pyflakespep8-command-args " ")))) + (last (when py-pyflakespep8-history + (let* ((lastcmd (car py-pyflakespep8-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (buffer-file-name) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pyflakespep8 like this: " + (if last + last + default) + 'py-pyflakespep8-history) + (read-string "Run pyflakespep8 like this: " + (if last + last + default) + 'py-pyflakespep8-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defun py-pyflakespep8-help () + "Display pyflakespep8 command line help messages. " + (interactive) + (set-buffer (get-buffer-create "*pyflakespep8-Help*")) + (erase-buffer) + (shell-command "pyflakespep8 --help" "*pyflakespep8-Help*")) + +;;; Pychecker +(defun py-pychecker-run (command) + "*Run pychecker (default on the file currently visited)." + (interactive + (let ((default + (if (buffer-file-name) + (format "%s %s %s" py-pychecker-command + (mapconcat 'identity py-pychecker-command-args " ") + (buffer-file-name)) + (format "%s %s" py-pychecker-command + (mapconcat 'identity py-pychecker-command-args " ")))) + (last (when py-pychecker-history + (let* ((lastcmd (car py-pychecker-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (buffer-file-name) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pychecker like this: " + (if last + last + default) + 'py-pychecker-history) + (read-string "Run pychecker like this: " + (if last + last + default) + 'py-pychecker-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +;;; python-mode skeletons +;; Derived from python.el, where it's instrumented as abbrev +;; Original code authored by Dave Love AFAIK + +(define-skeleton py-else + "Auxiliary skeleton." + nil + (unless (eq ?y (read-char "Add `else' clause? (y for yes or RET for no) ")) + (signal 'quit t)) + < "else:" \n) + +(define-skeleton py-if + "If condition " + "if " "if " str ":" \n + _ \n + ("other condition, %s: " + < "elif " str ":" \n + > _ \n nil) + '(py-else) | ^) + +(define-skeleton py-else + "Auxiliary skeleton." + nil + (unless (eq ?y (read-char "Add `else' clause? (y for yes or RET for no) ")) + (signal 'quit t)) + "else:" \n + > _ \n) + +(define-skeleton py-while + "Condition: " + "while " "while " str ":" \n + > -1 _ \n + '(py-else) | ^) + +(define-skeleton py-for + "Target, %s: " + "for " "for " str " in " (skeleton-read "Expression, %s: ") ":" \n + > -1 _ \n + '(py-else) | ^) + +(define-skeleton py-try/except + "Py-try/except skeleton " + "try:" "try:" \n + > -1 _ \n + ("Exception, %s: " + < "except " str '(python-target) ":" \n + > _ \n nil) + < "except:" \n + > _ \n + '(py-else) | ^) + +(define-skeleton py-target + "Auxiliary skeleton." + "Target, %s: " ", " str | -2) + +(define-skeleton py-try/finally + "Py-try/finally skeleton " + "try:" \n + > -1 _ \n + < "finally:" \n + > _ \n) + +(define-skeleton py-def + "Name: " + "def " str " (" ("Parameter, %s: " (unless (equal ?\( (char-before)) ", ") + str) "):" \n + "\"\"\"" - "\"\"\"" \n ; Fixme: extra space inserted -- why?). + > _ \n) + +(define-skeleton py-class + "Name: " + "class " str " (" ("Inheritance, %s: " + (unless (equal ?\( (char-before)) ", ") + str) + & ")" | -2 ; close list or remove opening + ":" \n + "\"\"\"" - "\"\"\"" \n + > _ \n) + +;;; Virtualenv --- Switching virtual python enviroments seamlessly +;; Thanks Gabriele Lanaro and all working on that +;; Url: http://github.com/gabrielelanaro/emacs-starter-kit +;; The installation is fairly easy, you have the load option, put this +;; in your .emacs: + +;; (load-file "/path/to/virtualenv.el") +;; +;; Otherwise you can do it with the load path: + +;; (add-to-list 'load-path "Path/to/virtualenv.el/containing/directory/" +;; (require 'virtualenv) + +;; The usage is very intuitive, to activate a virtualenv use + +;; M-x virtualenv-activate + +;; It will prompt you for the virtual environment path. +;; If you want to deactivate a virtual environment, use: + +;; M-x virtualenv-deactivate + +(defvar virtualenv-workon-home nil) + +(if (getenv "WORKON_HOME") + (setq virtualenv-workon-home (getenv "WORKON_HOME")) + (setq virtualenv-workon-home "~/.virtualenvs")) + +(defvar virtualenv-name nil) +(setq virtualenv-name nil) + +;;TODO: Move to a generic UTILITY or TOOL package +(defun virtualenv-filter (predicate sequence) + "Apply to each element of SEQUENCE the PREDICATE, if FUNCTION + returns non-nil append the element to the return value of + virtualenv-filter: a list" + (let ((retlist '())) + (dolist (element sequence) + (when (funcall predicate element) + (push element retlist))) + (nreverse retlist))) + +(defun virtualenv-append-path (dir var) + "Append DIR to a path-like varibale VAR, for example: + (virtualenv-append-path /usr/bin:/bin /home/test/bin) -> /home/test/bin:/usr/bin:/bin" + (concat (expand-file-name dir) + path-separator + var)) + +(defun virtualenv-add-to-path (dir) + "Add the specified path element to the Emacs PATH" + (setenv "PATH" + (virtualenv-append-path dir + (getenv "PATH")))) + +(defun virtualenv-current () + "barfs the current activated virtualenv" + (interactive) + (message virtualenv-name)) + +(defun virtualenv-activate (dir) + "Activate the virtualenv located in DIR" + (interactive "DVirtualenv Directory: ") + + ;; Eventually deactivate previous virtualenv + (when virtualenv-name + (virtualenv-deactivate)) + + ;; Storing old variables + (setq virtualenv-old-path (getenv "PATH")) + (setq virtualenv-old-exec-path exec-path) + + (setenv "VIRTUAL_ENV" dir) + (virtualenv-add-to-path (concat dir "bin")) + (add-to-list 'exec-path (concat dir "bin")) + + (setq virtualenv-name dir) + + (message (concat "Virtualenv '" virtualenv-name "' activated."))) + +(defun virtualenv-deactivate () + "Deactivate the current virtual enviroment" + (interactive) + + ;; Restoring old variables + (setenv "PATH" virtualenv-old-path) + (setq exec-path virtualenv-old-exec-path) + + (message (concat "Virtualenv '" virtualenv-name "' deactivated.")) + + (setq virtualenv-name nil)) + +(defun virtualenvp (dir) + "Check if a directory is a virtualenv" + (file-exists-p (concat dir "/bin/activate"))) + +(defun virtualenv-workon-complete () + "return available completions for virtualenv-workon" + (let + ;;Varlist + ((filelist (directory-files virtualenv-workon-home t))) + ;; Get only the basename from the list of the virtual environments + ;; paths + (mapcar 'file-name-nondirectory + ;; Filter the directories and then the virtual environments + (virtualenv-filter 'virtualenvp + (virtualenv-filter 'file-directory-p filelist))))) + +(defun virtualenv-workon (name) + "Issue a virtualenvwrapper-like virtualenv-workon command" + (interactive (list (completing-read "Virtualenv: " (virtualenv-workon-complete)))) + (virtualenv-activate (concat (getenv "WORKON_HOME") "/" name))) + +(defun py-toggle-local-default-use () + (interactive) + "Toggle boolean value of `py-use-local-default'. + +Returns `py-use-local-default' + +See also `py-install-local-shells' +Installing named virualenv shells is the preffered way, +as it leaves your system default unchanged." + (setq py-use-local-default (not py-use-local-default)) + (when (interactive-p) (message "py-use-local-default set to %s" py-use-local-default)) + py-use-local-default) + +;;; Extended executes +;; created by `write-extended-execute-forms' +(defun py-execute-prepare (form &optional shell dedicated switch) + "Used by python-extended-executes ." + (save-excursion + (let ((beg (prog1 + (or (funcall (intern-soft (concat "py-beginning-of-" form "-p"))) + + (funcall (intern-soft (concat "py-beginning-of-" form))) + (push-mark)))) + (end (funcall (intern-soft (concat "py-end-of-" form))))) + (py-execute-base beg end shell dedicated switch)))) + +(defun py-execute-statement-python () + "Send statement at point to Python interpreter. " + (interactive) + (py-execute-prepare "statement" "python" nil nil)) + +(defun py-execute-statement-python-switch () + "Send statement at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "python" nil 'switch)) + +(defun py-execute-statement-python-noswitch () + "Send statement at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "python" nil 'noswitch)) + +(defun py-execute-statement-python-dedicated () + "Send statement at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "statement" "python" t nil)) + +(defun py-execute-statement-python-dedicated-switch () + "Send statement at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "python" t 'switch)) + +(defun py-execute-statement-ipython () + "Send statement at point to IPython interpreter. " + (interactive) + (py-execute-prepare "statement" "ipython" nil nil)) + +(defun py-execute-statement-ipython-switch () + "Send statement at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "ipython" nil 'switch)) + +(defun py-execute-statement-ipython-noswitch () + "Send statement at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "ipython" nil 'noswitch)) + +(defun py-execute-statement-ipython-dedicated () + "Send statement at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "statement" "ipython" t nil)) + +(defun py-execute-statement-ipython-dedicated-switch () + "Send statement at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "ipython" t 'switch)) + +(defun py-execute-statement-python3 () + "Send statement at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "statement" "python3" nil nil)) + +(defun py-execute-statement-python3-switch () + "Send statement at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "python3" nil 'switch)) + +(defun py-execute-statement-python3-noswitch () + "Send statement at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "python3" nil 'noswitch)) + +(defun py-execute-statement-python3-dedicated () + "Send statement at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "statement" "python3" t nil)) + +(defun py-execute-statement-python3-dedicated-switch () + "Send statement at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "python3" t 'switch)) + +(defun py-execute-statement-python2 () + "Send statement at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "statement" "python2" nil nil)) + +(defun py-execute-statement-python2-switch () + "Send statement at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "python2" nil 'switch)) + +(defun py-execute-statement-python2-noswitch () + "Send statement at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "python2" nil 'noswitch)) + +(defun py-execute-statement-python2-dedicated () + "Send statement at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "statement" "python2" t nil)) + +(defun py-execute-statement-python2-dedicated-switch () + "Send statement at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "python2" t 'switch)) + +(defun py-execute-statement-python2.7 () + "Send statement at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "statement" "python2.7" nil nil)) + +(defun py-execute-statement-python2.7-switch () + "Send statement at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "python2.7" nil 'switch)) + +(defun py-execute-statement-python2.7-noswitch () + "Send statement at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "python2.7" nil 'noswitch)) + +(defun py-execute-statement-python2.7-dedicated () + "Send statement at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "statement" "python2.7" t nil)) + +(defun py-execute-statement-python2.7-dedicated-switch () + "Send statement at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "python2.7" t 'switch)) + +(defun py-execute-statement-jython () + "Send statement at point to Jython interpreter. " + (interactive) + (py-execute-prepare "statement" "jython" nil nil)) + +(defun py-execute-statement-jython-switch () + "Send statement at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "jython" nil 'switch)) + +(defun py-execute-statement-jython-noswitch () + "Send statement at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "jython" nil 'noswitch)) + +(defun py-execute-statement-jython-dedicated () + "Send statement at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "statement" "jython" t nil)) + +(defun py-execute-statement-jython-dedicated-switch () + "Send statement at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "jython" t 'switch)) + +(defun py-execute-statement-python3.2 () + "Send statement at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "statement" "python3.2" nil nil)) + +(defun py-execute-statement-python3.2-switch () + "Send statement at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "statement" "python3.2" nil 'switch)) + +(defun py-execute-statement-python3.2-noswitch () + "Send statement at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "statement" "python3.2" nil 'noswitch)) + +(defun py-execute-statement-python3.2-dedicated () + "Send statement at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "statement" "python3.2" t nil)) + +(defun py-execute-statement-python3.2-dedicated-switch () + "Send statement at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "statement" "python3.2" t 'switch)) + +(defun py-execute-block-python () + "Send block at point to Python interpreter. " + (interactive) + (py-execute-prepare "block" "python" nil nil)) + +(defun py-execute-block-python-switch () + "Send block at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "python" nil 'switch)) + +(defun py-execute-block-python-noswitch () + "Send block at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "python" nil 'noswitch)) + +(defun py-execute-block-python-dedicated () + "Send block at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "block" "python" t nil)) + +(defun py-execute-block-python-dedicated-switch () + "Send block at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "python" t 'switch)) + +(defun py-execute-block-ipython () + "Send block at point to IPython interpreter. " + (interactive) + (py-execute-prepare "block" "ipython" nil nil)) + +(defun py-execute-block-ipython-switch () + "Send block at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "ipython" nil 'switch)) + +(defun py-execute-block-ipython-noswitch () + "Send block at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "ipython" nil 'noswitch)) + +(defun py-execute-block-ipython-dedicated () + "Send block at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "block" "ipython" t nil)) + +(defun py-execute-block-ipython-dedicated-switch () + "Send block at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "ipython" t 'switch)) + +(defun py-execute-block-python3 () + "Send block at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "block" "python3" nil nil)) + +(defun py-execute-block-python3-switch () + "Send block at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "python3" nil 'switch)) + +(defun py-execute-block-python3-noswitch () + "Send block at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "python3" nil 'noswitch)) + +(defun py-execute-block-python3-dedicated () + "Send block at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "block" "python3" t nil)) + +(defun py-execute-block-python3-dedicated-switch () + "Send block at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "python3" t 'switch)) + +(defun py-execute-block-python2 () + "Send block at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "block" "python2" nil nil)) + +(defun py-execute-block-python2-switch () + "Send block at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "python2" nil 'switch)) + +(defun py-execute-block-python2-noswitch () + "Send block at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "python2" nil 'noswitch)) + +(defun py-execute-block-python2-dedicated () + "Send block at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "block" "python2" t nil)) + +(defun py-execute-block-python2-dedicated-switch () + "Send block at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "python2" t 'switch)) + +(defun py-execute-block-python2.7 () + "Send block at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "block" "python2.7" nil nil)) + +(defun py-execute-block-python2.7-switch () + "Send block at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "python2.7" nil 'switch)) + +(defun py-execute-block-python2.7-noswitch () + "Send block at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "python2.7" nil 'noswitch)) + +(defun py-execute-block-python2.7-dedicated () + "Send block at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "block" "python2.7" t nil)) + +(defun py-execute-block-python2.7-dedicated-switch () + "Send block at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "python2.7" t 'switch)) + +(defun py-execute-block-jython () + "Send block at point to Jython interpreter. " + (interactive) + (py-execute-prepare "block" "jython" nil nil)) + +(defun py-execute-block-jython-switch () + "Send block at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "jython" nil 'switch)) + +(defun py-execute-block-jython-noswitch () + "Send block at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "jython" nil 'noswitch)) + +(defun py-execute-block-jython-dedicated () + "Send block at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "block" "jython" t nil)) + +(defun py-execute-block-jython-dedicated-switch () + "Send block at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "jython" t 'switch)) + +(defun py-execute-block-python3.2 () + "Send block at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "block" "python3.2" nil nil)) + +(defun py-execute-block-python3.2-switch () + "Send block at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block" "python3.2" nil 'switch)) + +(defun py-execute-block-python3.2-noswitch () + "Send block at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block" "python3.2" nil 'noswitch)) + +(defun py-execute-block-python3.2-dedicated () + "Send block at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "block" "python3.2" t nil)) + +(defun py-execute-block-python3.2-dedicated-switch () + "Send block at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block" "python3.2" t 'switch)) + +(defun py-execute-clause-python () + "Send clause at point to Python interpreter. " + (interactive) + (py-execute-prepare "clause" "python" nil nil)) + +(defun py-execute-clause-python-switch () + "Send clause at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "python" nil 'switch)) + +(defun py-execute-clause-python-noswitch () + "Send clause at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "python" nil 'noswitch)) + +(defun py-execute-clause-python-dedicated () + "Send clause at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "clause" "python" t nil)) + +(defun py-execute-clause-python-dedicated-switch () + "Send clause at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "python" t 'switch)) + +(defun py-execute-clause-ipython () + "Send clause at point to IPython interpreter. " + (interactive) + (py-execute-prepare "clause" "ipython" nil nil)) + +(defun py-execute-clause-ipython-switch () + "Send clause at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "ipython" nil 'switch)) + +(defun py-execute-clause-ipython-noswitch () + "Send clause at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "ipython" nil 'noswitch)) + +(defun py-execute-clause-ipython-dedicated () + "Send clause at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "clause" "ipython" t nil)) + +(defun py-execute-clause-ipython-dedicated-switch () + "Send clause at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "ipython" t 'switch)) + +(defun py-execute-clause-python3 () + "Send clause at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "clause" "python3" nil nil)) + +(defun py-execute-clause-python3-switch () + "Send clause at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "python3" nil 'switch)) + +(defun py-execute-clause-python3-noswitch () + "Send clause at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "python3" nil 'noswitch)) + +(defun py-execute-clause-python3-dedicated () + "Send clause at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "clause" "python3" t nil)) + +(defun py-execute-clause-python3-dedicated-switch () + "Send clause at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "python3" t 'switch)) + +(defun py-execute-clause-python2 () + "Send clause at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "clause" "python2" nil nil)) + +(defun py-execute-clause-python2-switch () + "Send clause at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "python2" nil 'switch)) + +(defun py-execute-clause-python2-noswitch () + "Send clause at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "python2" nil 'noswitch)) + +(defun py-execute-clause-python2-dedicated () + "Send clause at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "clause" "python2" t nil)) + +(defun py-execute-clause-python2-dedicated-switch () + "Send clause at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "python2" t 'switch)) + +(defun py-execute-clause-python2.7 () + "Send clause at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "clause" "python2.7" nil nil)) + +(defun py-execute-clause-python2.7-switch () + "Send clause at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "python2.7" nil 'switch)) + +(defun py-execute-clause-python2.7-noswitch () + "Send clause at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "python2.7" nil 'noswitch)) + +(defun py-execute-clause-python2.7-dedicated () + "Send clause at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "clause" "python2.7" t nil)) + +(defun py-execute-clause-python2.7-dedicated-switch () + "Send clause at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "python2.7" t 'switch)) + +(defun py-execute-clause-jython () + "Send clause at point to Jython interpreter. " + (interactive) + (py-execute-prepare "clause" "jython" nil nil)) + +(defun py-execute-clause-jython-switch () + "Send clause at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "jython" nil 'switch)) + +(defun py-execute-clause-jython-noswitch () + "Send clause at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "jython" nil 'noswitch)) + +(defun py-execute-clause-jython-dedicated () + "Send clause at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "clause" "jython" t nil)) + +(defun py-execute-clause-jython-dedicated-switch () + "Send clause at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "jython" t 'switch)) + +(defun py-execute-clause-python3.2 () + "Send clause at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "clause" "python3.2" nil nil)) + +(defun py-execute-clause-python3.2-switch () + "Send clause at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "clause" "python3.2" nil 'switch)) + +(defun py-execute-clause-python3.2-noswitch () + "Send clause at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "clause" "python3.2" nil 'noswitch)) + +(defun py-execute-clause-python3.2-dedicated () + "Send clause at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "clause" "python3.2" t nil)) + +(defun py-execute-clause-python3.2-dedicated-switch () + "Send clause at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "clause" "python3.2" t 'switch)) + +(defun py-execute-block-or-clause-python () + "Send block-or-clause at point to Python interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python" nil nil)) + +(defun py-execute-block-or-clause-python-switch () + "Send block-or-clause at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "python" nil 'switch)) + +(defun py-execute-block-or-clause-python-noswitch () + "Send block-or-clause at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "python" nil 'noswitch)) + +(defun py-execute-block-or-clause-python-dedicated () + "Send block-or-clause at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python" t nil)) + +(defun py-execute-block-or-clause-python-dedicated-switch () + "Send block-or-clause at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "python" t 'switch)) + +(defun py-execute-block-or-clause-ipython () + "Send block-or-clause at point to IPython interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "ipython" nil nil)) + +(defun py-execute-block-or-clause-ipython-switch () + "Send block-or-clause at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "ipython" nil 'switch)) + +(defun py-execute-block-or-clause-ipython-noswitch () + "Send block-or-clause at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "ipython" nil 'noswitch)) + +(defun py-execute-block-or-clause-ipython-dedicated () + "Send block-or-clause at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "ipython" t nil)) + +(defun py-execute-block-or-clause-ipython-dedicated-switch () + "Send block-or-clause at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "ipython" t 'switch)) + +(defun py-execute-block-or-clause-python3 () + "Send block-or-clause at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python3" nil nil)) + +(defun py-execute-block-or-clause-python3-switch () + "Send block-or-clause at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "python3" nil 'switch)) + +(defun py-execute-block-or-clause-python3-noswitch () + "Send block-or-clause at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "python3" nil 'noswitch)) + +(defun py-execute-block-or-clause-python3-dedicated () + "Send block-or-clause at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python3" t nil)) + +(defun py-execute-block-or-clause-python3-dedicated-switch () + "Send block-or-clause at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "python3" t 'switch)) + +(defun py-execute-block-or-clause-python2 () + "Send block-or-clause at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python2" nil nil)) + +(defun py-execute-block-or-clause-python2-switch () + "Send block-or-clause at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "python2" nil 'switch)) + +(defun py-execute-block-or-clause-python2-noswitch () + "Send block-or-clause at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "python2" nil 'noswitch)) + +(defun py-execute-block-or-clause-python2-dedicated () + "Send block-or-clause at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python2" t nil)) + +(defun py-execute-block-or-clause-python2-dedicated-switch () + "Send block-or-clause at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "python2" t 'switch)) + +(defun py-execute-block-or-clause-python2.7 () + "Send block-or-clause at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python2.7" nil nil)) + +(defun py-execute-block-or-clause-python2.7-switch () + "Send block-or-clause at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "python2.7" nil 'switch)) + +(defun py-execute-block-or-clause-python2.7-noswitch () + "Send block-or-clause at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "python2.7" nil 'noswitch)) + +(defun py-execute-block-or-clause-python2.7-dedicated () + "Send block-or-clause at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python2.7" t nil)) + +(defun py-execute-block-or-clause-python2.7-dedicated-switch () + "Send block-or-clause at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "python2.7" t 'switch)) + +(defun py-execute-block-or-clause-jython () + "Send block-or-clause at point to Jython interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "jython" nil nil)) + +(defun py-execute-block-or-clause-jython-switch () + "Send block-or-clause at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "jython" nil 'switch)) + +(defun py-execute-block-or-clause-jython-noswitch () + "Send block-or-clause at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "jython" nil 'noswitch)) + +(defun py-execute-block-or-clause-jython-dedicated () + "Send block-or-clause at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "jython" t nil)) + +(defun py-execute-block-or-clause-jython-dedicated-switch () + "Send block-or-clause at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "jython" t 'switch)) + +(defun py-execute-block-or-clause-python3.2 () + "Send block-or-clause at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python3.2" nil nil)) + +(defun py-execute-block-or-clause-python3.2-switch () + "Send block-or-clause at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "block-or-clause" "python3.2" nil 'switch)) + +(defun py-execute-block-or-clause-python3.2-noswitch () + "Send block-or-clause at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "block-or-clause" "python3.2" nil 'noswitch)) + +(defun py-execute-block-or-clause-python3.2-dedicated () + "Send block-or-clause at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "block-or-clause" "python3.2" t nil)) + +(defun py-execute-block-or-clause-python3.2-dedicated-switch () + "Send block-or-clause at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "block-or-clause" "python3.2" t 'switch)) + +(defun py-execute-def-python () + "Send def at point to Python interpreter. " + (interactive) + (py-execute-prepare "def" "python" nil nil)) + +(defun py-execute-def-python-switch () + "Send def at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "python" nil 'switch)) + +(defun py-execute-def-python-noswitch () + "Send def at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "python" nil 'noswitch)) + +(defun py-execute-def-python-dedicated () + "Send def at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "def" "python" t nil)) + +(defun py-execute-def-python-dedicated-switch () + "Send def at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "python" t 'switch)) + +(defun py-execute-def-ipython () + "Send def at point to IPython interpreter. " + (interactive) + (py-execute-prepare "def" "ipython" nil nil)) + +(defun py-execute-def-ipython-switch () + "Send def at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "ipython" nil 'switch)) + +(defun py-execute-def-ipython-noswitch () + "Send def at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "ipython" nil 'noswitch)) + +(defun py-execute-def-ipython-dedicated () + "Send def at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "def" "ipython" t nil)) + +(defun py-execute-def-ipython-dedicated-switch () + "Send def at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "ipython" t 'switch)) + +(defun py-execute-def-python3 () + "Send def at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "def" "python3" nil nil)) + +(defun py-execute-def-python3-switch () + "Send def at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "python3" nil 'switch)) + +(defun py-execute-def-python3-noswitch () + "Send def at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "python3" nil 'noswitch)) + +(defun py-execute-def-python3-dedicated () + "Send def at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "def" "python3" t nil)) + +(defun py-execute-def-python3-dedicated-switch () + "Send def at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "python3" t 'switch)) + +(defun py-execute-def-python2 () + "Send def at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "def" "python2" nil nil)) + +(defun py-execute-def-python2-switch () + "Send def at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "python2" nil 'switch)) + +(defun py-execute-def-python2-noswitch () + "Send def at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "python2" nil 'noswitch)) + +(defun py-execute-def-python2-dedicated () + "Send def at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "def" "python2" t nil)) + +(defun py-execute-def-python2-dedicated-switch () + "Send def at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "python2" t 'switch)) + +(defun py-execute-def-python2.7 () + "Send def at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "def" "python2.7" nil nil)) + +(defun py-execute-def-python2.7-switch () + "Send def at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "python2.7" nil 'switch)) + +(defun py-execute-def-python2.7-noswitch () + "Send def at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "python2.7" nil 'noswitch)) + +(defun py-execute-def-python2.7-dedicated () + "Send def at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "def" "python2.7" t nil)) + +(defun py-execute-def-python2.7-dedicated-switch () + "Send def at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "python2.7" t 'switch)) + +(defun py-execute-def-jython () + "Send def at point to Jython interpreter. " + (interactive) + (py-execute-prepare "def" "jython" nil nil)) + +(defun py-execute-def-jython-switch () + "Send def at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "jython" nil 'switch)) + +(defun py-execute-def-jython-noswitch () + "Send def at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "jython" nil 'noswitch)) + +(defun py-execute-def-jython-dedicated () + "Send def at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "def" "jython" t nil)) + +(defun py-execute-def-jython-dedicated-switch () + "Send def at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "jython" t 'switch)) + +(defun py-execute-def-python3.2 () + "Send def at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "def" "python3.2" nil nil)) + +(defun py-execute-def-python3.2-switch () + "Send def at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "def" "python3.2" nil 'switch)) + +(defun py-execute-def-python3.2-noswitch () + "Send def at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "def" "python3.2" nil 'noswitch)) + +(defun py-execute-def-python3.2-dedicated () + "Send def at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "def" "python3.2" t nil)) + +(defun py-execute-def-python3.2-dedicated-switch () + "Send def at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "def" "python3.2" t 'switch)) + +(defun py-execute-class-python () + "Send class at point to Python interpreter. " + (interactive) + (py-execute-prepare "class" "python" nil nil)) + +(defun py-execute-class-python-switch () + "Send class at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "python" nil 'switch)) + +(defun py-execute-class-python-noswitch () + "Send class at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "python" nil 'noswitch)) + +(defun py-execute-class-python-dedicated () + "Send class at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "class" "python" t nil)) + +(defun py-execute-class-python-dedicated-switch () + "Send class at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "python" t 'switch)) + +(defun py-execute-class-ipython () + "Send class at point to IPython interpreter. " + (interactive) + (py-execute-prepare "class" "ipython" nil nil)) + +(defun py-execute-class-ipython-switch () + "Send class at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "ipython" nil 'switch)) + +(defun py-execute-class-ipython-noswitch () + "Send class at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "ipython" nil 'noswitch)) + +(defun py-execute-class-ipython-dedicated () + "Send class at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "class" "ipython" t nil)) + +(defun py-execute-class-ipython-dedicated-switch () + "Send class at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "ipython" t 'switch)) + +(defun py-execute-class-python3 () + "Send class at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "class" "python3" nil nil)) + +(defun py-execute-class-python3-switch () + "Send class at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "python3" nil 'switch)) + +(defun py-execute-class-python3-noswitch () + "Send class at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "python3" nil 'noswitch)) + +(defun py-execute-class-python3-dedicated () + "Send class at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "class" "python3" t nil)) + +(defun py-execute-class-python3-dedicated-switch () + "Send class at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "python3" t 'switch)) + +(defun py-execute-class-python2 () + "Send class at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "class" "python2" nil nil)) + +(defun py-execute-class-python2-switch () + "Send class at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "python2" nil 'switch)) + +(defun py-execute-class-python2-noswitch () + "Send class at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "python2" nil 'noswitch)) + +(defun py-execute-class-python2-dedicated () + "Send class at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "class" "python2" t nil)) + +(defun py-execute-class-python2-dedicated-switch () + "Send class at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "python2" t 'switch)) + +(defun py-execute-class-python2.7 () + "Send class at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "class" "python2.7" nil nil)) + +(defun py-execute-class-python2.7-switch () + "Send class at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "python2.7" nil 'switch)) + +(defun py-execute-class-python2.7-noswitch () + "Send class at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "python2.7" nil 'noswitch)) + +(defun py-execute-class-python2.7-dedicated () + "Send class at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "class" "python2.7" t nil)) + +(defun py-execute-class-python2.7-dedicated-switch () + "Send class at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "python2.7" t 'switch)) + +(defun py-execute-class-jython () + "Send class at point to Jython interpreter. " + (interactive) + (py-execute-prepare "class" "jython" nil nil)) + +(defun py-execute-class-jython-switch () + "Send class at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "jython" nil 'switch)) + +(defun py-execute-class-jython-noswitch () + "Send class at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "jython" nil 'noswitch)) + +(defun py-execute-class-jython-dedicated () + "Send class at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "class" "jython" t nil)) + +(defun py-execute-class-jython-dedicated-switch () + "Send class at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "jython" t 'switch)) + +(defun py-execute-class-python3.2 () + "Send class at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "class" "python3.2" nil nil)) + +(defun py-execute-class-python3.2-switch () + "Send class at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "class" "python3.2" nil 'switch)) + +(defun py-execute-class-python3.2-noswitch () + "Send class at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "class" "python3.2" nil 'noswitch)) + +(defun py-execute-class-python3.2-dedicated () + "Send class at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "class" "python3.2" t nil)) + +(defun py-execute-class-python3.2-dedicated-switch () + "Send class at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "class" "python3.2" t 'switch)) + +(defun py-execute-region-python (beg end) + "Send region at point to Python interpreter. " + (interactive "r") + (py-execute-base beg end "python" nil nil)) + +(defun py-execute-region-python-switch (beg end) + "Send region at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "python" nil 'switch)) + +(defun py-execute-region-python-noswitch (beg end) + "Send region at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "python" nil 'noswitch)) + +(defun py-execute-region-python-dedicated (beg end) + "Send region at point to Python unique interpreter. " + (interactive "r") + (py-execute-base beg end "python" t nil)) + +(defun py-execute-region-python-dedicated-switch (beg end) + "Send region at point to Python unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "python" t 'switch)) + +(defun py-execute-region-ipython (beg end) + "Send region at point to IPython interpreter. " + (interactive "r") + (py-execute-base beg end "ipython" nil nil)) + +(defun py-execute-region-ipython-switch (beg end) + "Send region at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "ipython" nil 'switch)) + +(defun py-execute-region-ipython-noswitch (beg end) + "Send region at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "ipython" nil 'noswitch)) + +(defun py-execute-region-ipython-dedicated (beg end) + "Send region at point to IPython unique interpreter. " + (interactive "r") + (py-execute-base beg end "ipython" t nil)) + +(defun py-execute-region-ipython-dedicated-switch (beg end) + "Send region at point to IPython unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "ipython" t 'switch)) + +(defun py-execute-region-python3 (beg end) + "Send region at point to Python3 interpreter. " + (interactive "r") + (py-execute-base beg end "python3" nil nil)) + +(defun py-execute-region-python3-switch (beg end) + "Send region at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "python3" nil 'switch)) + +(defun py-execute-region-python3-noswitch (beg end) + "Send region at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "python3" nil 'noswitch)) + +(defun py-execute-region-python3-dedicated (beg end) + "Send region at point to Python3 unique interpreter. " + (interactive "r") + (py-execute-base beg end "python3" t nil)) + +(defun py-execute-region-python3-dedicated-switch (beg end) + "Send region at point to Python3 unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "python3" t 'switch)) + +(defun py-execute-region-python2 (beg end) + "Send region at point to Python2 interpreter. " + (interactive "r") + (py-execute-base beg end "python2" nil nil)) + +(defun py-execute-region-python2-switch (beg end) + "Send region at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "python2" nil 'switch)) + +(defun py-execute-region-python2-noswitch (beg end) + "Send region at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "python2" nil 'noswitch)) + +(defun py-execute-region-python2-dedicated (beg end) + "Send region at point to Python2 unique interpreter. " + (interactive "r") + (py-execute-base beg end "python2" t nil)) + +(defun py-execute-region-python2-dedicated-switch (beg end) + "Send region at point to Python2 unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "python2" t 'switch)) + +(defun py-execute-region-python2.7 (beg end) + "Send region at point to Python2.7 interpreter. " + (interactive "r") + (py-execute-base beg end "python2.7" nil nil)) + +(defun py-execute-region-python2.7-switch (beg end) + "Send region at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "python2.7" nil 'switch)) + +(defun py-execute-region-python2.7-noswitch (beg end) + "Send region at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "python2.7" nil 'noswitch)) + +(defun py-execute-region-python2.7-dedicated (beg end) + "Send region at point to Python2.7 unique interpreter. " + (interactive "r") + (py-execute-base beg end "python2.7" t nil)) + +(defun py-execute-region-python2.7-dedicated-switch (beg end) + "Send region at point to Python2.7 unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "python2.7" t 'switch)) + +(defun py-execute-region-jython (beg end) + "Send region at point to Jython interpreter. " + (interactive "r") + (py-execute-base beg end "jython" nil nil)) + +(defun py-execute-region-jython-switch (beg end) + "Send region at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "jython" nil 'switch)) + +(defun py-execute-region-jython-noswitch (beg end) + "Send region at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "jython" nil 'noswitch)) + +(defun py-execute-region-jython-dedicated (beg end) + "Send region at point to Jython unique interpreter. " + (interactive "r") + (py-execute-base beg end "jython" t nil)) + +(defun py-execute-region-jython-dedicated-switch (beg end) + "Send region at point to Jython unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "jython" t 'switch)) + +(defun py-execute-region-python3.2 (beg end) + "Send region at point to Python3.2 interpreter. " + (interactive "r") + (py-execute-base beg end "python3.2" nil nil)) + +(defun py-execute-region-python3.2-switch (beg end) + "Send region at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive "r") + (py-execute-base beg end "python3.2" nil 'switch)) + +(defun py-execute-region-python3.2-noswitch (beg end) + "Send region at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive "r") + (py-execute-base beg end "python3.2" nil 'noswitch)) + +(defun py-execute-region-python3.2-dedicated (beg end) + "Send region at point to Python3.2 unique interpreter. " + (interactive "r") + (py-execute-base beg end "python3.2" t nil)) + +(defun py-execute-region-python3.2-dedicated-switch (beg end) + "Send region at point to Python3.2 unique interpreter and switch to result. " + (interactive "r") + (py-execute-base beg end "python3.2" t 'switch)) + +(defun py-execute-buffer-python () + "Send buffer at point to Python interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python" nil nil)))) + +(defun py-execute-buffer-python-switch () + "Send buffer at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python" nil 'switch)))) + +(defun py-execute-buffer-python-noswitch () + "Send buffer at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python" nil 'noswitch)))) + +(defun py-execute-buffer-python-dedicated () + "Send buffer at point to Python unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python" t nil)))) + +(defun py-execute-buffer-python-dedicated-switch () + "Send buffer at point to Python unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python" t 'switch)))) + +(defun py-execute-buffer-ipython () + "Send buffer at point to IPython interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "ipython" nil nil)))) + +(defun py-execute-buffer-ipython-switch () + "Send buffer at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "ipython" nil 'switch)))) + +(defun py-execute-buffer-ipython-noswitch () + "Send buffer at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "ipython" nil 'noswitch)))) + +(defun py-execute-buffer-ipython-dedicated () + "Send buffer at point to IPython unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "ipython" t nil)))) + +(defun py-execute-buffer-ipython-dedicated-switch () + "Send buffer at point to IPython unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "ipython" t 'switch)))) + +(defun py-execute-buffer-python3 () + "Send buffer at point to Python3 interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3" nil nil)))) + +(defun py-execute-buffer-python3-switch () + "Send buffer at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3" nil 'switch)))) + +(defun py-execute-buffer-python3-noswitch () + "Send buffer at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3" nil 'noswitch)))) + +(defun py-execute-buffer-python3-dedicated () + "Send buffer at point to Python3 unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3" t nil)))) + +(defun py-execute-buffer-python3-dedicated-switch () + "Send buffer at point to Python3 unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3" t 'switch)))) + +(defun py-execute-buffer-python2 () + "Send buffer at point to Python2 interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2" nil nil)))) + +(defun py-execute-buffer-python2-switch () + "Send buffer at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2" nil 'switch)))) + +(defun py-execute-buffer-python2-noswitch () + "Send buffer at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2" nil 'noswitch)))) + +(defun py-execute-buffer-python2-dedicated () + "Send buffer at point to Python2 unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2" t nil)))) + +(defun py-execute-buffer-python2-dedicated-switch () + "Send buffer at point to Python2 unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2" t 'switch)))) + +(defun py-execute-buffer-python2.7 () + "Send buffer at point to Python2.7 interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2.7" nil nil)))) + +(defun py-execute-buffer-python2.7-switch () + "Send buffer at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2.7" nil 'switch)))) + +(defun py-execute-buffer-python2.7-noswitch () + "Send buffer at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2.7" nil 'noswitch)))) + +(defun py-execute-buffer-python2.7-dedicated () + "Send buffer at point to Python2.7 unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2.7" t nil)))) + +(defun py-execute-buffer-python2.7-dedicated-switch () + "Send buffer at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python2.7" t 'switch)))) + +(defun py-execute-buffer-jython () + "Send buffer at point to Jython interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "jython" nil nil)))) + +(defun py-execute-buffer-jython-switch () + "Send buffer at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "jython" nil 'switch)))) + +(defun py-execute-buffer-jython-noswitch () + "Send buffer at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "jython" nil 'noswitch)))) + +(defun py-execute-buffer-jython-dedicated () + "Send buffer at point to Jython unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "jython" t nil)))) + +(defun py-execute-buffer-jython-dedicated-switch () + "Send buffer at point to Jython unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "jython" t 'switch)))) + +(defun py-execute-buffer-python3.2 () + "Send buffer at point to Python3.2 interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3.2" nil nil)))) + +(defun py-execute-buffer-python3.2-switch () + "Send buffer at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3.2" nil 'switch)))) + +(defun py-execute-buffer-python3.2-noswitch () + "Send buffer at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3.2" nil 'noswitch)))) + +(defun py-execute-buffer-python3.2-dedicated () + "Send buffer at point to Python3.2 unique interpreter. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3.2" t nil)))) + +(defun py-execute-buffer-python3.2-dedicated-switch () + "Send buffer at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (save-excursion + (let ((wholebuf t) + (py-master-file (or py-master-file (py-fetch-py-master-file))) + beg end) + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (setq beg (point-min)) + (setq end (point-max)) + (py-execute-region beg end "python3.2" t 'switch)))) + +(defun py-execute-expression-python () + "Send expression at point to Python interpreter. " + (interactive) + (py-execute-prepare "expression" "python" nil nil)) + +(defun py-execute-expression-python-switch () + "Send expression at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "python" nil 'switch)) + +(defun py-execute-expression-python-noswitch () + "Send expression at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "python" nil 'noswitch)) + +(defun py-execute-expression-python-dedicated () + "Send expression at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "expression" "python" t nil)) + +(defun py-execute-expression-python-dedicated-switch () + "Send expression at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "python" t 'switch)) + +(defun py-execute-expression-ipython () + "Send expression at point to IPython interpreter. " + (interactive) + (py-execute-prepare "expression" "ipython" nil nil)) + +(defun py-execute-expression-ipython-switch () + "Send expression at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "ipython" nil 'switch)) + +(defun py-execute-expression-ipython-noswitch () + "Send expression at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "ipython" nil 'noswitch)) + +(defun py-execute-expression-ipython-dedicated () + "Send expression at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "expression" "ipython" t nil)) + +(defun py-execute-expression-ipython-dedicated-switch () + "Send expression at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "ipython" t 'switch)) + +(defun py-execute-expression-python3 () + "Send expression at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "expression" "python3" nil nil)) + +(defun py-execute-expression-python3-switch () + "Send expression at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "python3" nil 'switch)) + +(defun py-execute-expression-python3-noswitch () + "Send expression at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "python3" nil 'noswitch)) + +(defun py-execute-expression-python3-dedicated () + "Send expression at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "expression" "python3" t nil)) + +(defun py-execute-expression-python3-dedicated-switch () + "Send expression at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "python3" t 'switch)) + +(defun py-execute-expression-python2 () + "Send expression at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "expression" "python2" nil nil)) + +(defun py-execute-expression-python2-switch () + "Send expression at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "python2" nil 'switch)) + +(defun py-execute-expression-python2-noswitch () + "Send expression at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "python2" nil 'noswitch)) + +(defun py-execute-expression-python2-dedicated () + "Send expression at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "expression" "python2" t nil)) + +(defun py-execute-expression-python2-dedicated-switch () + "Send expression at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "python2" t 'switch)) + +(defun py-execute-expression-python2.7 () + "Send expression at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "expression" "python2.7" nil nil)) + +(defun py-execute-expression-python2.7-switch () + "Send expression at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "python2.7" nil 'switch)) + +(defun py-execute-expression-python2.7-noswitch () + "Send expression at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "python2.7" nil 'noswitch)) + +(defun py-execute-expression-python2.7-dedicated () + "Send expression at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "expression" "python2.7" t nil)) + +(defun py-execute-expression-python2.7-dedicated-switch () + "Send expression at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "python2.7" t 'switch)) + +(defun py-execute-expression-jython () + "Send expression at point to Jython interpreter. " + (interactive) + (py-execute-prepare "expression" "jython" nil nil)) + +(defun py-execute-expression-jython-switch () + "Send expression at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "jython" nil 'switch)) + +(defun py-execute-expression-jython-noswitch () + "Send expression at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "jython" nil 'noswitch)) + +(defun py-execute-expression-jython-dedicated () + "Send expression at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "expression" "jython" t nil)) + +(defun py-execute-expression-jython-dedicated-switch () + "Send expression at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "jython" t 'switch)) + +(defun py-execute-expression-python3.2 () + "Send expression at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "expression" "python3.2" nil nil)) + +(defun py-execute-expression-python3.2-switch () + "Send expression at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "expression" "python3.2" nil 'switch)) + +(defun py-execute-expression-python3.2-noswitch () + "Send expression at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "expression" "python3.2" nil 'noswitch)) + +(defun py-execute-expression-python3.2-dedicated () + "Send expression at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "expression" "python3.2" t nil)) + +(defun py-execute-expression-python3.2-dedicated-switch () + "Send expression at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "expression" "python3.2" t 'switch)) + +(defun py-execute-partial-expression-python () + "Send partial-expression at point to Python interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python" nil nil)) + +(defun py-execute-partial-expression-python-switch () + "Send partial-expression at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "python" nil 'switch)) + +(defun py-execute-partial-expression-python-noswitch () + "Send partial-expression at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "python" nil 'noswitch)) + +(defun py-execute-partial-expression-python-dedicated () + "Send partial-expression at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python" t nil)) + +(defun py-execute-partial-expression-python-dedicated-switch () + "Send partial-expression at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "python" t 'switch)) + +(defun py-execute-partial-expression-ipython () + "Send partial-expression at point to IPython interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "ipython" nil nil)) + +(defun py-execute-partial-expression-ipython-switch () + "Send partial-expression at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "ipython" nil 'switch)) + +(defun py-execute-partial-expression-ipython-noswitch () + "Send partial-expression at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "ipython" nil 'noswitch)) + +(defun py-execute-partial-expression-ipython-dedicated () + "Send partial-expression at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "ipython" t nil)) + +(defun py-execute-partial-expression-ipython-dedicated-switch () + "Send partial-expression at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "ipython" t 'switch)) + +(defun py-execute-partial-expression-python3 () + "Send partial-expression at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python3" nil nil)) + +(defun py-execute-partial-expression-python3-switch () + "Send partial-expression at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "python3" nil 'switch)) + +(defun py-execute-partial-expression-python3-noswitch () + "Send partial-expression at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "python3" nil 'noswitch)) + +(defun py-execute-partial-expression-python3-dedicated () + "Send partial-expression at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python3" t nil)) + +(defun py-execute-partial-expression-python3-dedicated-switch () + "Send partial-expression at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "python3" t 'switch)) + +(defun py-execute-partial-expression-python2 () + "Send partial-expression at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python2" nil nil)) + +(defun py-execute-partial-expression-python2-switch () + "Send partial-expression at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "python2" nil 'switch)) + +(defun py-execute-partial-expression-python2-noswitch () + "Send partial-expression at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "python2" nil 'noswitch)) + +(defun py-execute-partial-expression-python2-dedicated () + "Send partial-expression at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python2" t nil)) + +(defun py-execute-partial-expression-python2-dedicated-switch () + "Send partial-expression at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "python2" t 'switch)) + +(defun py-execute-partial-expression-python2.7 () + "Send partial-expression at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python2.7" nil nil)) + +(defun py-execute-partial-expression-python2.7-switch () + "Send partial-expression at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "python2.7" nil 'switch)) + +(defun py-execute-partial-expression-python2.7-noswitch () + "Send partial-expression at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "python2.7" nil 'noswitch)) + +(defun py-execute-partial-expression-python2.7-dedicated () + "Send partial-expression at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python2.7" t nil)) + +(defun py-execute-partial-expression-python2.7-dedicated-switch () + "Send partial-expression at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "python2.7" t 'switch)) + +(defun py-execute-partial-expression-jython () + "Send partial-expression at point to Jython interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "jython" nil nil)) + +(defun py-execute-partial-expression-jython-switch () + "Send partial-expression at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "jython" nil 'switch)) + +(defun py-execute-partial-expression-jython-noswitch () + "Send partial-expression at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "jython" nil 'noswitch)) + +(defun py-execute-partial-expression-jython-dedicated () + "Send partial-expression at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "jython" t nil)) + +(defun py-execute-partial-expression-jython-dedicated-switch () + "Send partial-expression at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "jython" t 'switch)) + +(defun py-execute-partial-expression-python3.2 () + "Send partial-expression at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python3.2" nil nil)) + +(defun py-execute-partial-expression-python3.2-switch () + "Send partial-expression at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "partial-expression" "python3.2" nil 'switch)) + +(defun py-execute-partial-expression-python3.2-noswitch () + "Send partial-expression at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "partial-expression" "python3.2" nil 'noswitch)) + +(defun py-execute-partial-expression-python3.2-dedicated () + "Send partial-expression at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "partial-expression" "python3.2" t nil)) + +(defun py-execute-partial-expression-python3.2-dedicated-switch () + "Send partial-expression at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "partial-expression" "python3.2" t 'switch)) + +(defun py-execute-line-python () + "Send line at point to Python interpreter. " + (interactive) + (py-execute-prepare "line" "python" nil nil)) + +(defun py-execute-line-python-switch () + "Send line at point to Python interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "python" nil 'switch)) + +(defun py-execute-line-python-noswitch () + "Send line at point to Python interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "python" nil 'noswitch)) + +(defun py-execute-line-python-dedicated () + "Send line at point to Python unique interpreter. " + (interactive) + (py-execute-prepare "line" "python" t nil)) + +(defun py-execute-line-python-dedicated-switch () + "Send line at point to Python unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "python" t 'switch)) + +(defun py-execute-line-ipython () + "Send line at point to IPython interpreter. " + (interactive) + (py-execute-prepare "line" "ipython" nil nil)) + +(defun py-execute-line-ipython-switch () + "Send line at point to IPython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "ipython" nil 'switch)) + +(defun py-execute-line-ipython-noswitch () + "Send line at point to IPython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "ipython" nil 'noswitch)) + +(defun py-execute-line-ipython-dedicated () + "Send line at point to IPython unique interpreter. " + (interactive) + (py-execute-prepare "line" "ipython" t nil)) + +(defun py-execute-line-ipython-dedicated-switch () + "Send line at point to IPython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "ipython" t 'switch)) + +(defun py-execute-line-python3 () + "Send line at point to Python3 interpreter. " + (interactive) + (py-execute-prepare "line" "python3" nil nil)) + +(defun py-execute-line-python3-switch () + "Send line at point to Python3 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "python3" nil 'switch)) + +(defun py-execute-line-python3-noswitch () + "Send line at point to Python3 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "python3" nil 'noswitch)) + +(defun py-execute-line-python3-dedicated () + "Send line at point to Python3 unique interpreter. " + (interactive) + (py-execute-prepare "line" "python3" t nil)) + +(defun py-execute-line-python3-dedicated-switch () + "Send line at point to Python3 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "python3" t 'switch)) + +(defun py-execute-line-python2 () + "Send line at point to Python2 interpreter. " + (interactive) + (py-execute-prepare "line" "python2" nil nil)) + +(defun py-execute-line-python2-switch () + "Send line at point to Python2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "python2" nil 'switch)) + +(defun py-execute-line-python2-noswitch () + "Send line at point to Python2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "python2" nil 'noswitch)) + +(defun py-execute-line-python2-dedicated () + "Send line at point to Python2 unique interpreter. " + (interactive) + (py-execute-prepare "line" "python2" t nil)) + +(defun py-execute-line-python2-dedicated-switch () + "Send line at point to Python2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "python2" t 'switch)) + +(defun py-execute-line-python2.7 () + "Send line at point to Python2.7 interpreter. " + (interactive) + (py-execute-prepare "line" "python2.7" nil nil)) + +(defun py-execute-line-python2.7-switch () + "Send line at point to Python2.7 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "python2.7" nil 'switch)) + +(defun py-execute-line-python2.7-noswitch () + "Send line at point to Python2.7 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "python2.7" nil 'noswitch)) + +(defun py-execute-line-python2.7-dedicated () + "Send line at point to Python2.7 unique interpreter. " + (interactive) + (py-execute-prepare "line" "python2.7" t nil)) + +(defun py-execute-line-python2.7-dedicated-switch () + "Send line at point to Python2.7 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "python2.7" t 'switch)) + +(defun py-execute-line-jython () + "Send line at point to Jython interpreter. " + (interactive) + (py-execute-prepare "line" "jython" nil nil)) + +(defun py-execute-line-jython-switch () + "Send line at point to Jython interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "jython" nil 'switch)) + +(defun py-execute-line-jython-noswitch () + "Send line at point to Jython interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "jython" nil 'noswitch)) + +(defun py-execute-line-jython-dedicated () + "Send line at point to Jython unique interpreter. " + (interactive) + (py-execute-prepare "line" "jython" t nil)) + +(defun py-execute-line-jython-dedicated-switch () + "Send line at point to Jython unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "jython" t 'switch)) + +(defun py-execute-line-python3.2 () + "Send line at point to Python3.2 interpreter. " + (interactive) + (py-execute-prepare "line" "python3.2" nil nil)) + +(defun py-execute-line-python3.2-switch () + "Send line at point to Python3.2 interpreter. + +Switch to output buffer. Ignores `py-shell-switch-buffers-on-execute-p'. " + (interactive) + (py-execute-prepare "line" "python3.2" nil 'switch)) + +(defun py-execute-line-python3.2-noswitch () + "Send line at point to Python3.2 interpreter. + +Keep current buffer. Ignores `py-shell-switch-buffers-on-execute-p' " + (interactive) + (py-execute-prepare "line" "python3.2" nil 'noswitch)) + +(defun py-execute-line-python3.2-dedicated () + "Send line at point to Python3.2 unique interpreter. " + (interactive) + (py-execute-prepare "line" "python3.2" t nil)) + +(defun py-execute-line-python3.2-dedicated-switch () + "Send line at point to Python3.2 unique interpreter and switch to result. " + (interactive) + (py-execute-prepare "line" "python3.2" t 'switch)) + +;;; Column-marker - highlight columns +;; merged from column-marker.el, +;; Created: Tue Nov 22 10:26:03 2005 +;; Last-Updated: Fri Jan 22 11:28:48 2010 (-0800) By: dradams +;; original Author: Rick Bielawski <rbielaws@i1.net> + +(defface column-marker-1 '((t (:background "gray"))) + "Face used for a column marker. Usually a background color." + :group 'faces) + +(defvar column-marker-1-face 'column-marker-1 + "Face used for a column marker. Usually a background color. +Changing this directly affects only new markers.") + +(defface column-marker-2 '((t (:background "cyan3"))) + "Face used for a column marker. Usually a background color." + :group 'faces) + +(defvar column-marker-2-face 'column-marker-2 + "Face used for a column marker. Usually a background color. +Changing this directly affects only new markers." ) + +(defface column-marker-3 '((t (:background "orchid3"))) + "Face used for a column marker. Usually a background color." + :group 'faces) + +(defvar column-marker-3-face 'column-marker-3 + "Face used for a column marker. Usually a background color. +Changing this directly affects only new markers." ) + +(defvar column-marker-vars () + "List of all internal column-marker variables") +(make-variable-buffer-local 'column-marker-vars) ; Buffer local in all buffers. + +(defmacro column-marker-create (var &optional face) + "Define a column marker named VAR. +FACE is the face to use. If nil, then face `column-marker-1' is used." + (setq face (or face 'column-marker-1)) + `(progn + ;; define context variable ,VAR so marker can be removed if desired + (defvar ,var () + "Buffer local. Used internally to store column marker spec.") + ;; context must be buffer local since font-lock is + (make-variable-buffer-local ',var) + ;; Define wrapper function named ,VAR to call `column-marker-internal' + (defun ,var (arg) + ,(concat "Highlight column with face `" (symbol-name face) + "'.\nWith no prefix argument, highlight current column.\n" + "With non-negative numeric prefix arg, highlight that column number.\n" + "With plain `C-u' (no number), turn off this column marker.\n" + "With `C-u C-u' or negative prefix arg, turn off all column-marker highlighting.") + (interactive "P") + (unless (memq ',var column-marker-vars) (push ',var column-marker-vars)) + (cond ((null arg) ; Default: highlight current column. + (column-marker-internal ',var (1+ (current-column)) ,face)) + ((consp arg) + (if (= 4 (car arg)) + (column-marker-internal ',var nil) ; `C-u': Remove this column highlighting. + (dolist (var column-marker-vars) + (column-marker-internal var nil)))) ; `C-u C-u': Remove all column highlighting. + ((and (integerp arg) (>= arg 0)) ; `C-u 70': Highlight that column. + (column-marker-internal ',var (1+ (prefix-numeric-value arg)) ,face)) + (t ; `C-u -40': Remove all column highlighting. + (dolist (var column-marker-vars) + (column-marker-internal var nil))))))) + +(defun column-marker-find (col) + "Defines a function to locate a character in column COL. +Returns the function symbol, named `column-marker-move-to-COL'." + (let ((fn-symb (intern (format "column-marker-move-to-%d" col)))) + (fset `,fn-symb + `(lambda (end) + (let ((start (point))) + (when (> end (point-max)) (setq end (point-max))) + + ;; Try to keep `move-to-column' from going backward, though it still can. + (unless (< (current-column) ,col) (forward-line 1)) + + ;; Again, don't go backward. Try to move to correct column. + (when (< (current-column) ,col) (move-to-column ,col)) + + ;; If not at target column, try to move to it. + (while (and (< (current-column) ,col) (< (point) end) + (= 0 (+ (forward-line 1) (current-column)))) ; Should be bol. + (move-to-column ,col)) + + ;; If at target column, not past end, and not prior to start, + ;; then set match data and return t. Otherwise go to start + ;; and return nil. + (if (and (= ,col (current-column)) (<= (point) end) (> (point) start)) + (progn (set-match-data (list (1- (point)) (point))) + t) ; Return t. + (goto-char start) + nil)))) ; Return nil. + fn-symb)) + +(defun column-marker-internal (sym col &optional face) + "SYM is the symbol for holding the column marker context. +COL is the column in which a marker should be set. +Supplying nil or 0 for COL turns off the marker. +FACE is the face to use. If nil, then face `column-marker-1' is used." + (setq face (or face 'column-marker-1)) + (when (symbol-value sym) ; Remove any previously set column marker + (font-lock-remove-keywords nil (symbol-value sym)) + (set sym nil)) + (when (or (listp col) (< col 0)) (setq col nil)) ; Allow nonsense stuff to turn off the marker + (when col ; Generate a new column marker + (set sym `((,(column-marker-find col) (0 ',face prepend t)))) + (font-lock-add-keywords nil (symbol-value sym) t)) + (font-lock-fontify-buffer)) + +;; If you need more markers you can create your own similarly. +;; All markers can be in use at once, and each is buffer-local, +;; so there is no good reason to define more unless you need more +;; markers in a single buffer. +(column-marker-create column-marker-1 column-marker-1-face) +(column-marker-create column-marker-2 column-marker-2-face) +(column-marker-create column-marker-3 column-marker-3-face) + +;; (autoload 'column-marker-1 "column-marker" "Highlight a column." t) + +(defalias 'ipython-send-and-indent 'py-execute-line-ipython) +(defalias 'py-execute-region-in-shell 'py-execute-region) +(defalias 'py-shell-command-on-region 'py-execute-region-region) +(defalias 'py-send-region-ipython 'py-execute-region-ipython) +(defalias 'py-ipython-shell-command-on-region 'py-execute-region-ipython) +(provide 'python-mode) +;;; python-mode.el ends here
new file mode 100644 --- /dev/null +++ b/plugins/tabbar.el @@ -0,0 +1,1932 @@ +;;; Tabbar.el --- Display a tab bar in the header line + +;; Copyright (C) 2003, 2004, 2005 David Ponce + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> +;; Created: 25 February 2003 +;; Keywords: convenience +;; Revision: $Id: tabbar.el,v 1.2 2007-08-08 22:24:29 psg Exp $ + +(defconst tabbar-version "2.0") + +;; This file is not part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, or (at +;; your option) any later version. + +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; This library provides the Tabbar global minor mode to display a tab +;; bar in the header line of Emacs 21 and later versions. You can use +;; the mouse to click on a tab and select it. Also, three buttons are +;; displayed on the left side of the tab bar in this order: the +;; "home", "scroll left", and "scroll right" buttons. The "home" +;; button is a general purpose button used to change something on the +;; tab bar. The scroll left and scroll right buttons are used to +;; scroll tabs horizontally. Tabs can be divided up into groups to +;; maintain several sets of tabs at the same time (see also the +;; chapter "Core" below for more details on tab grouping). Only one +;; group is displayed on the tab bar, and the "home" button, for +;; example, can be used to navigate through the different groups, to +;; show different tab bars. +;; +;; In a graphic environment, using the mouse is probably the preferred +;; way to work with the tab bar. However, you can also use the tab +;; bar when Emacs is running on a terminal, so it is possible to use +;; commands to press special buttons, or to navigate cyclically +;; through tabs. +;; +;; These commands, and default keyboard shortcuts, are provided: +;; +;; `tabbar-mode' +;; Toggle the Tabbar global minor mode. When enabled a tab bar is +;; displayed in the header line. +;; +;; `tabbar-local-mode' (C-c <C-f10>) +;; Toggle the Tabbar-Local minor mode. Provided the global minor +;; mode is turned on, the tab bar becomes local in the current +;; buffer when the local minor mode is enabled. This permits to +;; see the tab bar in a buffer where the header line is already +;; used by another mode (like `Info-mode' for example). +;; +;; `tabbar-mwheel-mode' +;; Toggle the Tabbar-Mwheel global minor mode. When enabled you +;; can use the mouse wheel to navigate through tabs of groups. +;; +;; `tabbar-press-home' (C-c <C-home>) +;; `tabbar-press-scroll-left' (C-c <C-prior>) +;; `tabbar-press-scroll-right' (C-c <C-next>) +;; Simulate a mouse-1 click on respectively the "home", "scroll +;; left", and "scroll right" buttons. A numeric prefix argument +;; value of 2, or 3, respectively simulates a mouse-2, or mouse-3 +;; click. +;; +;; `tabbar-backward' (C-c <C-left>) +;; `tabbar-forward' (C-c <C-right>) +;; Are the basic commands to navigate cyclically through tabs or +;; groups of tabs. The cycle is controlled by the +;; `tabbar-cycle-scope' option. The default is to navigate +;; through all tabs across all existing groups of tabs. You can +;; change the default behavior to navigate only through the tabs +;; visible on the tab bar, or through groups of tabs only. Or use +;; the more specialized commands below. +;; +;; `tabbar-backward-tab' +;; `tabbar-forward-tab' +;; Navigate through the tabs visible on the tab bar. +;; +;; `tabbar-backward-group' (C-c <C-up>) +;; `tabbar-forward-group' (C-c <C-down>) +;; Navigate through existing groups of tabs. +;; +;; +;; Core +;; ---- +;; +;; The content of the tab bar is represented by an internal data +;; structure: a tab set. A tab set is a collection (group) of tabs, +;; identified by an unique name. In a tab set, at any time, one and +;; only one tab is designated as selected within the tab set. +;; +;; A tab is a simple data structure giving the value of the tab, and a +;; reference to its tab set container. A tab value can be any Lisp +;; object. Each tab object is guaranteed to be unique. +;; +;; A tab set is displayed on the tab bar through a "view" defined by +;; the index of the leftmost tab shown. Thus, it is possible to +;; scroll the tab bar horizontally by changing the start index of the +;; tab set view. +;; +;; The visual representation of a tab bar is a list of valid +;; `header-line-format' template elements, one for each special +;; button, and for each tab found into a tab set "view". When the +;; visual representation of a tab is required, the function specified +;; in the variable `tabbar-tab-label-function' is called to obtain it. +;; The visual representation of a special button is obtained by +;; calling the function specified in `tabbar-button-label-function', +;; which is passed a button name among `home', `scroll-left', or +;; `scroll-right'. There are also options and faces to customize the +;; appearance of buttons and tabs (see the code for more details). +;; +;; When the mouse is over a tab, the function specified in +;; `tabbar-help-on-tab-function' is called, which is passed the tab +;; and should return a help string to display. When a tab is +;; selected, the function specified in `tabbar-select-tab-function' is +;; called, which is passed the tab and the event received. +;; +;; Similarly, to control the behavior of the special buttons, the +;; following variables are available, for respectively the `home', +;; `scroll-left' and `scroll-right' value of `<button>': +;; +;; `tabbar-<button>-function' +;; Function called when <button> is selected. The function is +;; passed the mouse event received. +;; +;; `tabbar-<button>-help-function' +;; Function called with no arguments to obtain a help string +;; displayed when the mouse is over <button>. +;; +;; To increase performance, each tab set automatically maintains its +;; visual representation in a cache. As far as possible, the cache is +;; used to display the tab set, and refreshed only when necessary. +;; +;; Several tab sets can be maintained at the same time. Only one is +;; displayed on the tab bar, it is obtained by calling the function +;; specified in the variable `tabbar-current-tabset-function'. +;; +;; A special tab set is maintained, that contains the list of the +;; currently selected tabs in the existing tab sets. This tab set is +;; useful to show the existing tab sets in a tab bar, and switch +;; between them easily. The function `tabbar-get-tabsets-tabset' +;; returns this special tab set. +;; +;; +;; Buffer tabs +;; ----------- +;; +;; The default tab bar implementation provided displays buffers in +;; dedicated tabs. Selecting a tab, switch (mouse-1), or pop +;; (mouse-2), to the buffer it contains. +;; +;; The list of buffers put in tabs is provided by the function +;; specified in the variable `tabbar-buffer-list-function'. The +;; default function: `tabbar-buffer-list', excludes buffers whose name +;; starts with a space, when they are not visiting a file. +;; +;; Buffers are organized in groups, each one represented by a tab set. +;; A buffer can have no group, or belong to more than one group. The +;; function specified by the variable `tabbar-buffer-groups-function' +;; is called for each buffer to obtain the groups it belongs to. The +;; default function provided: `tabbar-buffer-groups' organizes buffers +;; depending on their major mode (see that function for details). +;; +;; The "home" button toggles display of buffer groups on the tab bar, +;; allowing to easily show another buffer group by clicking on the +;; associated tab. +;; +;; Known problems: +;; +;; Bug item #858306 at <http://sf.net/tracker/?group_id=79309>: +;; tabbar-mode crashes GNU Emacs 21.3 on MS-Windows 98/95. +;; + +;;; History: +;; + +;;; Code: + +;;; Options +;; +(defgroup tabbar nil + "Display a tab bar in the header line." + :group 'convenience) + +(defcustom tabbar-cycle-scope nil + "*Specify the scope of cyclic navigation through tabs. +The following scopes are possible: + +- `tabs' + Navigate through visible tabs only. +- `groups' + Navigate through tab groups only. +- default + Navigate through visible tabs, then through tab groups." + :group 'tabbar + :type '(choice :tag "Cycle through..." + (const :tag "Visible Tabs Only" tabs) + (const :tag "Tab Groups Only" groups) + (const :tag "Visible Tabs then Tab Groups" nil))) + +(defcustom tabbar-auto-scroll-flag t + "*Non-nil means to automatically scroll the tab bar. +That is, when a tab is selected outside of the tab bar visible area, +the tab bar is scrolled horizontally so the selected tab becomes +visible." + :group 'tabbar + :type 'boolean) + +(defvar tabbar-inhibit-functions '(tabbar-default-inhibit-function) + "List of functions to be called before displaying the tab bar. +Those functions are called one by one, with no arguments, until one of +them returns a non-nil value, and thus, prevents to display the tab +bar.") + +(defvar tabbar-current-tabset-function nil + "Function called with no argument to obtain the current tab set. +This is the tab set displayed on the tab bar.") + +(defvar tabbar-tab-label-function nil + "Function that obtains a tab label displayed on the tab bar. +The function is passed a tab and should return a string.") + +(defvar tabbar-select-tab-function nil + "Function that select a tab. +The function is passed a mouse event and a tab, and should make it the +selected tab.") + +(defvar tabbar-help-on-tab-function nil + "Function to obtain a help string for a tab. +The help string is displayed when the mouse is onto the button. The +function is passed the tab and should return a help string or nil for +none.") + +(defvar tabbar-button-label-function nil + "Function that obtains a button label displayed on the tab bar. +The function is passed a button name should return a propertized +string to display.") + +(defvar tabbar-home-function nil + "Function called when clicking on the tab bar home button. +The function is passed the mouse event received.") + +(defvar tabbar-home-help-function nil + "Function to obtain a help string for the tab bar home button. +The help string is displayed when the mouse is onto the button. +The function is called with no arguments.") + +(defvar tabbar-scroll-left-function 'tabbar-scroll-left + "Function that scrolls tabs on left. +The function is passed the mouse event received when clicking on the +scroll left button. It should scroll the current tab set.") + +(defvar tabbar-scroll-left-help-function 'tabbar-scroll-left-help + "Function to obtain a help string for the scroll left button. +The help string is displayed when the mouse is onto the button. +The function is called with no arguments.") + +(defvar tabbar-scroll-right-function 'tabbar-scroll-right + "Function that scrolls tabs on right. +The function is passed the mouse event received when clicking on the +scroll right button. It should scroll the current tab set.") + +(defvar tabbar-scroll-right-help-function 'tabbar-scroll-right-help + "Function to obtain a help string for the scroll right button. +The help string is displayed when the mouse is onto the button. +The function is called with no arguments.") + +;;; Misc. +;; +(eval-and-compile + (defalias 'tabbar-display-update + (if (fboundp 'force-window-update) + #'(lambda () (force-window-update (selected-window))) + 'force-mode-line-update))) + +(defsubst tabbar-click-p (event) + "Return non-nil if EVENT is a mouse click event." + (memq 'click (event-modifiers event))) + +(defun tabbar-shorten (str width) + "Return a shortened string from STR that fits in the given display WIDTH. +WIDTH is specified in terms of character display width in the current +buffer; see also `char-width'. If STR display width is greater than +WIDTH, STR is truncated and an ellipsis string \"...\" is inserted at +end or in the middle of the returned string, depending on available +room." + (let* ((n (length str)) + (sw (string-width str)) + (el "...") + (ew (string-width el)) + (w 0) + (i 0)) + (cond + ;; STR fit in WIDTH, return it. + ((<= sw width) + str) + ;; There isn't enough room for the ellipsis, STR is just + ;; truncated to fit in WIDTH. + ((<= width ew) + (while (< w width) + (setq w (+ w (char-width (aref str i))) + i (1+ i))) + (substring str 0 i)) + ;; There isn't enough room to insert the ellipsis in the middle + ;; of the truncated string, so put the ellipsis at end. + ((zerop (setq sw (/ (- width ew) 2))) + (setq width (- width ew)) + (while (< w width) + (setq w (+ w (char-width (aref str i))) + i (1+ i))) + (concat (substring str 0 i) el)) + ;; Put the ellipsis in the middle of the truncated string. + (t + (while (< w sw) + (setq w (+ w (char-width (aref str i))) + i (1+ i))) + (setq w (+ w ew)) + (while (< w width) + (setq n (1- n) + w (+ w (char-width (aref str n))))) + (concat (substring str 0 i) el (substring str n))) + ))) + +;;; Tab and tab set +;; +(defsubst tabbar-make-tab (object tabset) + "Return a new tab with value OBJECT. +TABSET is the tab set the tab belongs to." + (cons object tabset)) + +(defsubst tabbar-tab-value (tab) + "Return the value of tab TAB." + (car tab)) + +(defsubst tabbar-tab-tabset (tab) + "Return the tab set TAB belongs to." + (cdr tab)) + +(defvar tabbar-tabsets nil + "The tab sets store.") + +(defvar tabbar-tabsets-tabset nil + "The special tab set of existing tab sets.") + +(defvar tabbar-current-tabset nil + "The tab set currently displayed on the tab bar.") +(make-variable-buffer-local 'tabbar-current-tabset) + +(defvar tabbar-init-hook nil + "Hook run after tab bar data has been initialized. +You should use this hook to initialize dependent data.") + +(defsubst tabbar-init-tabsets-store () + "Initialize the tab set store." + (setq tabbar-tabsets (make-vector 31 0) + tabbar-tabsets-tabset (make-symbol "tabbar-tabsets-tabset")) + (put tabbar-tabsets-tabset 'start 0) + (run-hooks 'tabbar-init-hook)) + +(defvar tabbar-quit-hook nil + "Hook run after tab bar data has been freed. +You should use this hook to reset dependent data.") + +(defsubst tabbar-free-tabsets-store () + "Free the tab set store." + (setq tabbar-tabsets nil + tabbar-tabsets-tabset nil) + (run-hooks 'tabbar-quit-hook)) + +;; Define an "hygienic" function free of side effect between its local +;; variables and those of the callee. +(eval-and-compile + (defalias 'tabbar-map-tabsets + (let ((function (make-symbol "function")) + (result (make-symbol "result")) + (tabset (make-symbol "tabset"))) + `(lambda (,function) + "Apply FUNCTION to each tab set, and make a list of the results. +The result is a list just as long as the number of existing tab sets." + (let (,result) + (mapatoms + #'(lambda (,tabset) + (push (funcall ,function ,tabset) ,result)) + tabbar-tabsets) + ,result))))) + +(defun tabbar-make-tabset (name &rest objects) + "Make a new tab set whose name is the string NAME. +It is initialized with tabs build from the list of OBJECTS." + (let* ((tabset (intern name tabbar-tabsets)) + (tabs (mapcar #'(lambda (object) + (tabbar-make-tab object tabset)) + objects))) + (set tabset tabs) + (put tabset 'select (car tabs)) + (put tabset 'start 0) + tabset)) + +(defsubst tabbar-get-tabset (name) + "Return the tab set whose name is the string NAME. +Return nil if not found." + (intern-soft name tabbar-tabsets)) + +(defsubst tabbar-delete-tabset (tabset) + "Delete the tab set TABSET. +That is, remove it from the tab sets store." + (unintern tabset tabbar-tabsets)) + +(defsubst tabbar-tabs (tabset) + "Return the list of tabs in TABSET." + (symbol-value tabset)) + +(defsubst tabbar-tab-values (tabset) + "Return the list of tab values in TABSET." + (mapcar 'tabbar-tab-value (tabbar-tabs tabset))) + +(defsubst tabbar-get-tab (object tabset) + "Search for a tab with value OBJECT in TABSET. +Return the tab found, or nil if not found." + (assoc object (tabbar-tabs tabset))) + +(defsubst tabbar-member (tab tabset) + "Return non-nil if TAB is in TABSET." + (or (eq (tabbar-tab-tabset tab) tabset) + (memq tab (tabbar-tabs tabset)))) + +(defsubst tabbar-template (tabset) + "Return the cached visual representation of TABSET. +That is, a `header-line-format' template, or nil if the cache is +empty." + (get tabset 'template)) + +(defsubst tabbar-set-template (tabset template) + "Set the cached visual representation of TABSET to TEMPLATE. +TEMPLATE must be a valid `header-line-format' template, or nil to +cleanup the cache." + (put tabset 'template template)) + +(defsubst tabbar-selected-tab (tabset) + "Return the tab selected in TABSET." + (get tabset 'select)) + +(defsubst tabbar-selected-value (tabset) + "Return the value of the tab selected in TABSET." + (tabbar-tab-value (tabbar-selected-tab tabset))) + +(defsubst tabbar-selected-p (tab tabset) + "Return non-nil if TAB is the selected tab in TABSET." + (eq tab (tabbar-selected-tab tabset))) + +(defvar tabbar--track-selected nil) + +(defsubst tabbar-select-tab (tab tabset) + "Make TAB the selected tab in TABSET. +Does nothing if TAB is not found in TABSET. +Return TAB if selected, nil if not." + (when (tabbar-member tab tabset) + (unless (tabbar-selected-p tab tabset) + (tabbar-set-template tabset nil) + (setq tabbar--track-selected tabbar-auto-scroll-flag)) + (put tabset 'select tab))) + +(defsubst tabbar-select-tab-value (object tabset) + "Make the tab with value OBJECT, the selected tab in TABSET. +Does nothing if a tab with value OBJECT is not found in TABSET. +Return the tab selected, or nil if nothing was selected." + (tabbar-select-tab (tabbar-get-tab object tabset) tabset)) + +(defsubst tabbar-start (tabset) + "Return the index of the first visible tab in TABSET." + (get tabset 'start)) + +(defsubst tabbar-view (tabset) + "Return the list of visible tabs in TABSET. +That is, the sub-list of tabs starting at the first visible one." + (nthcdr (tabbar-start tabset) (tabbar-tabs tabset))) + +(defun tabbar-add-tab (tabset object &optional append) + "Add to TABSET a tab with value OBJECT if there isn't one there yet. +If the tab is added, it is added at the beginning of the tab list, +unless the optional argument APPEND is non-nil, in which case it is +added at the end." + (let ((tabs (tabbar-tabs tabset))) + (if (tabbar-get-tab object tabset) + tabs + (let ((tab (tabbar-make-tab object tabset))) + (tabbar-set-template tabset nil) + (set tabset (if append + (append tabs (list tab)) + (cons tab tabs))))))) + +(defun tabbar-delete-tab (tab) + "Remove TAB from its tab set." + (let* ((tabset (tabbar-tab-tabset tab)) + (tabs (tabbar-tabs tabset)) + (sel (eq tab (tabbar-selected-tab tabset))) + (next (and sel (cdr (memq tab tabs))))) + (tabbar-set-template tabset nil) + (setq tabs (delq tab tabs)) + ;; When the selected tab is deleted, select the next one, if + ;; available, or the last one otherwise. + (and sel (tabbar-select-tab (car (or next (last tabs))) tabset)) + (set tabset tabs))) + +(defun tabbar-scroll (tabset count) + "Scroll the visible tabs in TABSET of COUNT units. +If COUNT is positive move the view on right. If COUNT is negative, +move the view on left." + (let ((start (min (max 0 (+ (tabbar-start tabset) count)) + (1- (length (tabbar-tabs tabset)))))) + (when (/= start (tabbar-start tabset)) + (tabbar-set-template tabset nil) + (put tabset 'start start)))) + +(defun tabbar-tab-next (tabset tab &optional before) + "Search in TABSET for the tab after TAB. +If optional argument BEFORE is non-nil, search for the tab before +TAB. Return the tab found, or nil otherwise." + (let* (last (tabs (tabbar-tabs tabset))) + (while (and tabs (not (eq tab (car tabs)))) + (setq last (car tabs) + tabs (cdr tabs))) + (and tabs (if before last (nth 1 tabs))))) + +(defun tabbar-current-tabset (&optional update) + "Return the tab set currently displayed on the tab bar. +If optional argument UPDATE is non-nil, call the user defined function +`tabbar-current-tabset-function' to obtain it. Otherwise return the +current cached copy." + (and update tabbar-current-tabset-function + (setq tabbar-current-tabset + (funcall tabbar-current-tabset-function))) + tabbar-current-tabset) + +(defun tabbar-get-tabsets-tabset () + "Return the tab set of selected tabs in existing tab sets." + (set tabbar-tabsets-tabset (tabbar-map-tabsets 'tabbar-selected-tab)) + (tabbar-scroll tabbar-tabsets-tabset 0) + (tabbar-set-template tabbar-tabsets-tabset nil) + tabbar-tabsets-tabset) + +;;; Faces +;; +(defface tabbar-default + '( + ;;(((class color grayscale) (background light)) + ;; :inherit variable-pitch + ;; :height 0.8 + ;; :foreground "gray50" + ;; :background "grey75" + ;; ) + (((class color grayscale) (background dark)) + :inherit variable-pitch + :height 0.8 + :foreground "grey75" + :background "gray50" + ) + (((class mono) (background light)) + :inherit variable-pitch + :height 0.8 + :foreground "black" + :background "white" + ) + (((class mono) (background dark)) + :inherit variable-pitch + :height 0.8 + :foreground "white" + :background "black" + ) + (t + :inherit variable-pitch + :height 0.8 + :foreground "gray50" + :background "gray75" + )) + "Default face used in the tab bar." + :group 'tabbar) + +(defface tabbar-unselected + '((t + :inherit tabbar-default + :box (:line-width 1 :color "white" :style released-button) + )) + "Face used for unselected tabs." + :group 'tabbar) + +(defface tabbar-selected + '((t + :inherit tabbar-default + :box (:line-width 1 :color "white" :style pressed-button) + :foreground "blue" + )) + "Face used for the selected tab." + :group 'tabbar) + +(defface tabbar-highlight + '((t + :underline t + )) + "Face used to highlight a tab during mouse-overs." + :group 'tabbar) + +(defface tabbar-separator + '((t + :inherit tabbar-default + :height 0.1 + )) + "Face used for separators between tabs." + :group 'tabbar) + +(defface tabbar-button + '((t + :inherit tabbar-default + :box (:line-width 1 :color "white" :style released-button) + :foreground "dark red" + )) + "Face used for tab bar buttons." + :group 'tabbar) + +(defface tabbar-button-highlight + '((t + :inherit tabbar-default + )) + "Face used to highlight a button during mouse-overs." + :group 'tabbar) + +(defcustom tabbar-background-color nil + "*Background color of the tab bar. +By default, use the background color specified for the +`tabbar-default' face (or inherited from another face), or the +background color of the `default' face otherwise." + :group 'tabbar + :type '(choice (const :tag "Default" nil) + (color))) + +(defsubst tabbar-background-color () + "Return the background color of the tab bar." + (or tabbar-background-color + (let* ((face 'tabbar-default) + (color (face-background face))) + (while (null color) + (or (facep (setq face (face-attribute face :inherit))) + (setq face 'default)) + (setq color (face-background face))) + color))) + +;;; Buttons and separator look and feel +;; +(defconst tabbar-button-widget + '(cons + (cons :tag "Enabled" + (string) + (repeat :tag "Image" + :extra-offset 2 + (restricted-sexp :tag "Spec" + :match-alternatives (listp)))) + (cons :tag "Disabled" + (string) + (repeat :tag "Image" + :extra-offset 2 + (restricted-sexp :tag "Spec" + :match-alternatives (listp)))) + ) + "Widget for editing a tab bar button. +A button is specified as a pair (ENABLED-BUTTON . DISABLED-BUTTON), +where ENABLED-BUTTON and DISABLED-BUTTON specify the value used when +the button is respectively enabled and disabled. Each button value is +a pair (STRING . IMAGE) where STRING is a string value, and IMAGE a +list of image specifications. +If IMAGE is non-nil, try to use that image, else use STRING. +If only the ENABLED-BUTTON image is provided, a DISABLED-BUTTON image +is derived from it.") + +;;; Home button +;; +(defvar tabbar-home-button-value nil + "Value of the home button.") + +(defconst tabbar-home-button-enabled-image + '((:type pbm :data "\ +P2 13 13 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 +6 0 255 255 255 255 255 255 255 255 255 255 9 130 9 255 255 255 255 +255 255 255 255 255 255 26 130 26 255 255 255 255 255 255 255 0 9 26 +41 130 41 26 9 0 255 255 255 255 5 145 140 135 130 125 120 115 5 255 +255 255 255 0 9 26 41 130 41 26 9 0 255 255 255 255 255 255 255 26 130 +26 255 255 255 255 255 255 255 255 255 255 9 130 9 255 255 255 255 255 +255 255 255 255 255 0 6 0 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 +")) + "Default image for the enabled home button.") + +(defconst tabbar-home-button-disabled-image + '((:type pbm :data "\ +P2 13 13 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 0 0 1 2 3 2 1 0 0 255 255 255 255 0 132 128 123 119 114 110 +106 0 255 255 255 255 0 0 1 2 3 2 1 0 0 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 +")) + "Default image for the disabled home button.") + +(defcustom tabbar-home-button + (cons (cons "[o]" tabbar-home-button-enabled-image) + (cons "[x]" tabbar-home-button-disabled-image)) + "The home button. +The variable `tabbar-button-widget' gives details on this widget." + :group 'tabbar + :type tabbar-button-widget + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of button value. + (setq tabbar-home-button-value nil))) + +;;; Scroll left button +;; +(defvar tabbar-scroll-left-button-value nil + "Value of the scroll left button.") + +(defconst tabbar-scroll-left-button-enabled-image + '((:type pbm :data "\ +P2 13 13 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 128 16 48 255 255 255 255 255 255 255 +255 144 28 86 128 0 255 255 255 255 255 255 160 44 92 159 135 113 0 +255 255 255 255 160 44 97 165 144 129 120 117 0 255 255 176 44 98 175 +174 146 127 126 127 128 0 255 255 0 160 184 156 143 136 134 135 137 +138 0 255 255 176 32 67 144 146 144 145 146 148 149 0 255 255 255 255 +160 42 75 140 154 158 159 160 0 255 255 255 255 255 255 160 40 74 154 +170 171 0 255 255 255 255 255 255 255 255 160 41 82 163 0 255 255 255 +255 255 255 255 255 255 255 160 32 48 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 +")) + "Default image for the enabled scroll left button. +A disabled button image will be automatically build from it.") + +(defcustom tabbar-scroll-left-button + (cons (cons " <" tabbar-scroll-left-button-enabled-image) + (cons " =" nil)) + "The scroll left button. +The variable `tabbar-button-widget' gives details on this widget." + :group 'tabbar + :type tabbar-button-widget + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of button value. + (setq tabbar-scroll-left-button-value nil))) + +;;; Scroll right button +;; +(defvar tabbar-scroll-right-button-value nil + "Value of the scroll right button.") + +(defconst tabbar-scroll-right-button-enabled-image + '((:type pbm :data "\ +P2 13 13 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +48 32 160 255 255 255 255 255 255 255 255 255 255 44 161 71 32 160 255 +255 255 255 255 255 255 255 36 157 163 145 62 32 160 255 255 255 255 +255 255 30 128 133 137 142 124 50 32 160 255 255 255 255 29 120 121 +124 126 126 124 105 42 32 176 255 255 31 126 127 128 128 128 128 126 +124 89 32 255 255 33 134 135 136 137 137 138 119 49 32 176 255 255 34 +143 144 145 146 128 54 32 160 255 255 255 255 36 152 153 134 57 32 160 +255 255 255 255 255 255 38 141 60 32 160 255 255 255 255 255 255 255 +255 48 32 160 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 +")) + "Default image for the enabled scroll right button. +A disabled button image will be automatically build from it.") + +(defcustom tabbar-scroll-right-button + (cons (cons " >" tabbar-scroll-right-button-enabled-image) + (cons " =" nil)) + "The scroll right button. +The variable `tabbar-button-widget' gives details on this widget." + :group 'tabbar + :type tabbar-button-widget + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of button value. + (setq tabbar-scroll-right-button-value nil))) + +;;; Separator +;; +(defconst tabbar-separator-widget + '(cons (choice (string) + (number :tag "Space width" 0.2)) + (repeat :tag "Image" + :extra-offset 2 + (restricted-sexp :tag "Spec" + :match-alternatives (listp)))) + "Widget for editing a tab bar separator. +A separator is specified as a pair (STRING-OR-WIDTH . IMAGE) where +STRING-OR-WIDTH is a string value or a space width, and IMAGE a list +of image specifications. +If IMAGE is non-nil, try to use that image, else use STRING-OR-WIDTH. +The value (\"\"), or (0) hide separators.") + +(defvar tabbar-separator-value nil + "Value of the separator used between tabs.") + +(defcustom tabbar-separator (list 0.2) + "Separator used between tabs. +The variable `tabbar-separator-widget' gives details on this widget." + :group 'tabbar + :type tabbar-separator-widget + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of separator value. + (setq tabbar-separator-value nil))) + +;;; Images +;; +(defcustom tabbar-use-images t + "*Non-nil means to try to use images in tab bar. +That is for buttons and separators." + :group 'tabbar + :type 'boolean + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of all buttons and separator values. + (setq tabbar-separator-value nil + tabbar-home-button-value nil + tabbar-scroll-left-button-value nil + tabbar-scroll-right-button-value nil))) + +(defsubst tabbar-find-image (specs) + "Find an image, choosing one of a list of image specifications. +SPECS is a list of image specifications. See also `find-image'." + (when (and tabbar-use-images (display-images-p)) + (condition-case nil + (find-image specs) + (error nil)))) + +(defsubst tabbar-disable-image (image) + "From IMAGE, return a new image which looks disabled." + (setq image (copy-sequence image)) + (setcdr image (plist-put (cdr image) :conversion 'disabled)) + image) + +(defsubst tabbar-normalize-image (image &optional margin) + "Make IMAGE centered and transparent. +If optional MARGIN is non-nil, it must be a number of pixels to add as +an extra margin around the image." + (let ((plist (cdr image))) + (or (plist-get plist :ascent) + (setq plist (plist-put plist :ascent 'center))) + (or (plist-get plist :mask) + (setq plist (plist-put plist :mask '(heuristic t)))) + (or (not (natnump margin)) + (plist-get plist :margin) + (plist-put plist :margin margin)) + (setcdr image plist)) + image) + +;;; Button keymaps and callbacks +;; +(defun tabbar-make-mouse-keymap (callback) + "Return a keymap that call CALLBACK on mouse events. +CALLBACK is passed the received mouse event." + (let ((keymap (make-sparse-keymap))) + ;; Pass mouse-1, mouse-2 and mouse-3 events to CALLBACK. + (define-key keymap [header-line down-mouse-1] 'ignore) + (define-key keymap [header-line mouse-1] callback) + (define-key keymap [header-line down-mouse-2] 'ignore) + (define-key keymap [header-line mouse-2] callback) + (define-key keymap [header-line down-mouse-3] 'ignore) + (define-key keymap [header-line mouse-3] callback) + keymap)) + +(defsubst tabbar-make-mouse-event (&optional type) + "Return a mouse click event. +Optional argument TYPE is a mouse-click event or one of the +symbols `mouse-1', `mouse-2' or `mouse-3'. +The default is `mouse-1'." + (if (tabbar-click-p type) + type + (list (or (memq type '(mouse-2 mouse-3)) 'mouse-1) + (or (event-start nil) ;; Emacs 21.4 + (list (selected-window) (point) '(0 . 0) 0))))) + +;;; Buttons +;; +(defconst tabbar-default-button-keymap + (tabbar-make-mouse-keymap 'tabbar-select-button-callback) + "Default keymap of a button.") + +(defun tabbar-help-on-button (window object position) + "Return a help string or nil for none, for the button under the mouse. +WINDOW is the window in which the help was found (unused). +OBJECT is the button label under the mouse. +POSITION is the position in that label. +Call `tabbar-NAME-help-function' where NAME is the button name +associated to OBJECT." + (let* ((name (get-text-property position 'tabbar-button object)) + (funvar (and name + (intern-soft (format "tabbar-%s-help-function" + name))))) + (and (symbol-value funvar) + (funcall (symbol-value funvar))))) + +(defsubst tabbar-click-on-button (name &optional type) + "Handle a mouse click event on button NAME. +Call `tabbar-select-NAME-function' with the received, or simulated +mouse click event. +Optional argument TYPE is a mouse click event type (see the function +`tabbar-make-mouse-event' for details)." + (let ((funvar (intern-soft (format "tabbar-%s-function" name)))) + (when (symbol-value funvar) + (funcall (symbol-value funvar) (tabbar-make-mouse-event type)) + (tabbar-display-update)))) + +(defun tabbar-select-button-callback (event) + "Handle a mouse EVENT on a button. +Pass mouse click events on a button to `tabbar-click-on-button'." + (interactive "@e") + (when (tabbar-click-p event) + (let ((target (posn-string (event-start event)))) + (tabbar-click-on-button + (get-text-property (cdr target) 'tabbar-button (car target)) + event)))) + +(defun tabbar-make-button-keymap (name) + "Return a keymap to handle mouse click events on button NAME." + (if (fboundp 'posn-string) + tabbar-default-button-keymap + (let ((event (make-symbol "event"))) + (tabbar-make-mouse-keymap + `(lambda (,event) + (interactive "@e") + (and (tabbar-click-p ,event) + (tabbar-click-on-button ',name ,event))))))) + +;;; Button callbacks +;; +(defun tabbar-scroll-left (event) + "On mouse EVENT, scroll current tab set on left." + (when (eq (event-basic-type event) 'mouse-1) + (tabbar-scroll (tabbar-current-tabset) -1))) + +(defun tabbar-scroll-left-help () + "Help string shown when mouse is over the scroll left button." + "mouse-1: scroll tabs left.") + +(defun tabbar-scroll-right (event) + "On mouse EVENT, scroll current tab set on right." + (when (eq (event-basic-type event) 'mouse-1) + (tabbar-scroll (tabbar-current-tabset) 1))) + +(defun tabbar-scroll-right-help () + "Help string shown when mouse is over the scroll right button." + "mouse-1: scroll tabs right.") + +;;; Tabs +;; +(defconst tabbar-default-tab-keymap + (tabbar-make-mouse-keymap 'tabbar-select-tab-callback) + "Default keymap of a tab.") + +(defun tabbar-help-on-tab (window object position) + "Return a help string or nil for none, for the tab under the mouse. +WINDOW is the window in which the help was found (unused). +OBJECT is the tab label under the mouse. +POSITION is the position in that label. +Call `tabbar-help-on-tab-function' with the associated tab." + (when tabbar-help-on-tab-function + (let ((tab (get-text-property position 'tabbar-tab object))) + (funcall tabbar-help-on-tab-function tab)))) + +(defsubst tabbar-click-on-tab (tab &optional type) + "Handle a mouse click event on tab TAB. +Call `tabbar-select-tab-function' with the received, or simulated +mouse click event, and TAB. +Optional argument TYPE is a mouse click event type (see the function +`tabbar-make-mouse-event' for details)." + (when tabbar-select-tab-function + (funcall tabbar-select-tab-function + (tabbar-make-mouse-event type) tab) + (tabbar-display-update))) + +(defun tabbar-select-tab-callback (event) + "Handle a mouse EVENT on a tab. +Pass mouse click events on a tab to `tabbar-click-on-tab'." + (interactive "@e") + (when (tabbar-click-p event) + (let ((target (posn-string (event-start event)))) + (tabbar-click-on-tab + (get-text-property (cdr target) 'tabbar-tab (car target)) + event)))) + +(defun tabbar-make-tab-keymap (tab) + "Return a keymap to handle mouse click events on TAB." + (if (fboundp 'posn-string) + tabbar-default-tab-keymap + (let ((event (make-symbol "event"))) + (tabbar-make-mouse-keymap + `(lambda (,event) + (interactive "@e") + (and (tabbar-click-p ,event) + (tabbar-click-on-tab ',tab ,event))))))) + +;;; Tab bar construction +;; +(defun tabbar-button-label (name) + "Return a label for button NAME. +That is a pair (ENABLED . DISABLED), where ENABLED and DISABLED are +respectively the appearance of the button when enabled and disabled. +They are propertized strings which could display images, as specified +by the variable `tabbar-NAME-button'." + (let* ((btn (symbol-value + (intern-soft (format "tabbar-%s-button" name)))) + (on (tabbar-find-image (cdar btn))) + (off (and on (tabbar-find-image (cddr btn))))) + (when on + (tabbar-normalize-image on 1) + (if off + (tabbar-normalize-image off 1) + ;; If there is no disabled button image, derive one from the + ;; button enabled image. + (setq off (tabbar-disable-image on)))) + (cons + (propertize (or (caar btn) " ") 'display on) + (propertize (or (cadr btn) " ") 'display off)))) + +(defun tabbar-line-button (name) + "Return the display representation of button NAME. +That is, a propertized string used as an `header-line-format' template +element." + (let ((label (if tabbar-button-label-function + (funcall tabbar-button-label-function name) + (cons name name)))) + ;; Cache the display value of the enabled/disabled buttons in + ;; variables `tabbar-NAME-button-value'. + (set (intern (format "tabbar-%s-button-value" name)) + (cons + (propertize (car label) + 'tabbar-button name + 'face 'tabbar-button + 'mouse-face 'tabbar-button-highlight + 'pointer 'hand + 'local-map (tabbar-make-button-keymap name) + 'help-echo 'tabbar-help-on-button) + (propertize (cdr label) + 'face 'tabbar-button + 'pointer 'arrow))))) + +(defun tabbar-line-separator () + "Return the display representation of a tab bar separator. +That is, a propertized string used as an `header-line-format' template +element." + (let ((image (tabbar-find-image (cdr tabbar-separator)))) + ;; Cache the separator display value in variable + ;; `tabbar-separator-value'. + (setq tabbar-separator-value + (cond + (image + (propertize " " + 'face 'tabbar-separator + 'pointer 'arrow + 'display (tabbar-normalize-image image))) + ((numberp (car tabbar-separator)) + (propertize " " + 'face 'tabbar-separator + 'pointer 'arrow + 'display (list 'space + :width (car tabbar-separator)))) + ((propertize (or (car tabbar-separator) " ") + 'face 'tabbar-separator + 'pointer 'arrow)))) + )) + +(defsubst tabbar-line-buttons (tabset) + "Return a list of propertized strings for tab bar buttons. +TABSET is the tab set used to choose the appropriate buttons." + (list + (if tabbar-home-function + (car tabbar-home-button-value) + (cdr tabbar-home-button-value)) + (if (> (tabbar-start tabset) 0) + (car tabbar-scroll-left-button-value) + (cdr tabbar-scroll-left-button-value)) + (if (< (tabbar-start tabset) + (1- (length (tabbar-tabs tabset)))) + (car tabbar-scroll-right-button-value) + (cdr tabbar-scroll-right-button-value)) + tabbar-separator-value)) + +(defsubst tabbar-line-tab (tab) + "Return the display representation of tab TAB. +That is, a propertized string used as an `header-line-format' template +element. +Call `tabbar-tab-label-function' to obtain a label for TAB." + (concat (propertize + (if tabbar-tab-label-function + (funcall tabbar-tab-label-function tab) + tab) + 'tabbar-tab tab + 'local-map (tabbar-make-tab-keymap tab) + 'help-echo 'tabbar-help-on-tab + 'mouse-face 'tabbar-highlight + 'face (if (tabbar-selected-p tab (tabbar-current-tabset)) + 'tabbar-selected + 'tabbar-unselected) + 'pointer 'hand) + tabbar-separator-value)) + +(defun tabbar-line-format (tabset) + "Return the `header-line-format' value to display TABSET." + (let* ((sel (tabbar-selected-tab tabset)) + (tabs (tabbar-view tabset)) + (padcolor (tabbar-background-color)) + atsel elts) + ;; Initialize buttons and separator values. + (or tabbar-separator-value + (tabbar-line-separator)) + (or tabbar-home-button-value + (tabbar-line-button 'home)) + (or tabbar-scroll-left-button-value + (tabbar-line-button 'scroll-left)) + (or tabbar-scroll-right-button-value + (tabbar-line-button 'scroll-right)) + ;; Track the selected tab to ensure it is always visible. + (when tabbar--track-selected + (while (not (memq sel tabs)) + (tabbar-scroll tabset -1) + (setq tabs (tabbar-view tabset))) + (while (and tabs (not atsel)) + (setq elts (cons (tabbar-line-tab (car tabs)) elts) + atsel (eq (car tabs) sel) + tabs (cdr tabs))) + (setq elts (nreverse elts)) + ;; At this point the selected tab is the last elt in ELTS. + ;; Scroll TABSET and ELTS until the selected tab becomes + ;; visible. + (with-temp-buffer + (let ((truncate-partial-width-windows nil) + (inhibit-modification-hooks t) + deactivate-mark ;; Prevent deactivation of the mark! + start) + (setq truncate-lines nil + buffer-undo-list t) + (apply 'insert (tabbar-line-buttons tabset)) + (setq start (point)) + (while (and (cdr elts) ;; Always show the selected tab! + (progn + (delete-region start (point-max)) + (goto-char (point-max)) + (apply 'insert elts) + (goto-char (point-min)) + (> (vertical-motion 1) 0))) + (tabbar-scroll tabset 1) + (setq elts (cdr elts))))) + (setq elts (nreverse elts)) + (setq tabbar--track-selected nil)) + ;; Format remaining tabs. + (while tabs + (setq elts (cons (tabbar-line-tab (car tabs)) elts) + tabs (cdr tabs))) + ;; Cache and return the new tab bar. + (tabbar-set-template + tabset + (list (tabbar-line-buttons tabset) + (nreverse elts) + (propertize "%-" + 'face (list :background padcolor + :foreground padcolor) + 'pointer 'arrow))) + )) + +(defun tabbar-line () + "Return the header line templates that represent the tab bar. +Inhibit display of the tab bar in current window if any of the +`tabbar-inhibit-functions' return non-nil." + (cond + ((run-hook-with-args-until-success 'tabbar-inhibit-functions) + ;; Don't show the tab bar. + (setq header-line-format nil)) + ((tabbar-current-tabset t) + ;; When available, use a cached tab bar value, else recompute it. + (or (tabbar-template tabbar-current-tabset) + (tabbar-line-format tabbar-current-tabset))))) + +(defconst tabbar-header-line-format '(:eval (tabbar-line)) + "The tab bar header line format.") + +(defun tabbar-default-inhibit-function () + "Inhibit display of the tab bar in specified windows. +That is dedicated windows, and `checkdoc' status windows." + (or (window-dedicated-p (selected-window)) + (member (buffer-name) + (list " *Checkdoc Status*" + (if (boundp 'ispell-choices-buffer) + ispell-choices-buffer + "*Choices*"))))) + +;;; Cyclic navigation through tabs +;; +(defun tabbar-cycle (&optional backward type) + "Cycle to the next available tab. +The scope of the cyclic navigation through tabs is specified by the +option `tabbar-cycle-scope'. +If optional argument BACKWARD is non-nil, cycle to the previous tab +instead. +Optional argument TYPE is a mouse event type (see the function +`tabbar-make-mouse-event' for details)." + (let* ((tabset (tabbar-current-tabset t)) + (ttabset (tabbar-get-tabsets-tabset)) + ;; If navigation through groups is requested, and there is + ;; only one group, navigate through visible tabs. + (cycle (if (and (eq tabbar-cycle-scope 'groups) + (not (cdr (tabbar-tabs ttabset)))) + 'tabs + tabbar-cycle-scope)) + selected tab) + (when tabset + (setq selected (tabbar-selected-tab tabset)) + (cond + ;; Cycle through visible tabs only. + ((eq cycle 'tabs) + (setq tab (tabbar-tab-next tabset selected backward)) + ;; When there is no tab after/before the selected one, cycle + ;; to the first/last visible tab. + (unless tab + (setq tabset (tabbar-tabs tabset) + tab (car (if backward (last tabset) tabset)))) + ) + ;; Cycle through tab groups only. + ((eq cycle 'groups) + (setq tab (tabbar-tab-next ttabset selected backward)) + ;; When there is no group after/before the selected one, cycle + ;; to the first/last available group. + (unless tab + (setq tabset (tabbar-tabs ttabset) + tab (car (if backward (last tabset) tabset)))) + ) + (t + ;; Cycle through visible tabs then tab groups. + (setq tab (tabbar-tab-next tabset selected backward)) + ;; When there is no visible tab after/before the selected one, + ;; cycle to the next/previous available group. + (unless tab + (setq tab (tabbar-tab-next ttabset selected backward)) + ;; When there is no next/previous group, cycle to the + ;; first/last available group. + (unless tab + (setq tabset (tabbar-tabs ttabset) + tab (car (if backward (last tabset) tabset)))) + ;; Select the first/last visible tab of the new group. + (setq tabset (tabbar-tabs (tabbar-tab-tabset tab)) + tab (car (if backward (last tabset) tabset)))) + )) + (tabbar-click-on-tab tab type)))) + +;;;###autoload +(defun tabbar-backward () + "Select the previous available tab. +Depend on the setting of the option `tabbar-cycle-scope'." + (interactive) + (tabbar-cycle t)) + +;;;###autoload +(defun tabbar-forward () + "Select the next available tab. +Depend on the setting of the option `tabbar-cycle-scope'." + (interactive) + (tabbar-cycle)) + +;;;###autoload +(defun tabbar-backward-group () + "Go to selected tab in the previous available group." + (interactive) + (let ((tabbar-cycle-scope 'groups)) + (tabbar-cycle t))) + +;;;###autoload +(defun tabbar-forward-group () + "Go to selected tab in the next available group." + (interactive) + (let ((tabbar-cycle-scope 'groups)) + (tabbar-cycle))) + +;;;###autoload +(defun tabbar-backward-tab () + "Select the previous visible tab." + (interactive) + (let ((tabbar-cycle-scope 'tabs)) + (tabbar-cycle t))) + +;;;###autoload +(defun tabbar-forward-tab () + "Select the next visible tab." + (interactive) + (let ((tabbar-cycle-scope 'tabs)) + (tabbar-cycle))) + +;;; Button press commands +;; +(defsubst tabbar--mouse (number) + "Return a mouse button symbol from NUMBER. +That is mouse-2, or mouse-3 when NUMBER is respectively 2, or 3. +Return mouse-1 otherwise." + (cond ((eq number 2) 'mouse-2) + ((eq number 3) 'mouse-3) + ('mouse-1))) + +;;;###autoload +(defun tabbar-press-home (&optional arg) + "Press the tab bar home button. +That is, simulate a mouse click on that button. +A numeric prefix ARG value of 2, or 3, respectively simulates a +mouse-2, or mouse-3 click. The default is a mouse-1 click." + (interactive "p") + (tabbar-click-on-button 'home (tabbar--mouse arg))) + +;;;###autoload +(defun tabbar-press-scroll-left (&optional arg) + "Press the tab bar scroll-left button. +That is, simulate a mouse click on that button. +A numeric prefix ARG value of 2, or 3, respectively simulates a +mouse-2, or mouse-3 click. The default is a mouse-1 click." + (interactive "p") + (tabbar-click-on-button 'scroll-left (tabbar--mouse arg))) + +;;;###autoload +(defun tabbar-press-scroll-right (&optional arg) + "Press the tab bar scroll-right button. +That is, simulate a mouse click on that button. +A numeric prefix ARG value of 2, or 3, respectively simulates a +mouse-2, or mouse-3 click. The default is a mouse-1 click." + (interactive "p") + (tabbar-click-on-button 'scroll-right (tabbar--mouse arg))) + +;;; Mouse-wheel support +;; +(require 'mwheel) + +;;; Compatibility +;; +(defconst tabbar--mwheel-up-event + (symbol-value (if (boundp 'mouse-wheel-up-event) + 'mouse-wheel-up-event + 'mouse-wheel-up-button))) + +(defconst tabbar--mwheel-down-event + (symbol-value (if (boundp 'mouse-wheel-down-event) + 'mouse-wheel-down-event + 'mouse-wheel-down-button))) + +(defsubst tabbar--mwheel-key (event-type) + "Return a mouse wheel key symbol from EVENT-TYPE. +When EVENT-TYPE is a symbol return it. +When it is a button number, return symbol `mouse-<EVENT-TYPE>'." + (if (symbolp event-type) + event-type + (intern (format "mouse-%s" event-type)))) + +(defsubst tabbar--mwheel-up-p (event) + "Return non-nil if EVENT is a mouse-wheel up event." + (let ((x (event-basic-type event))) + (if (eq 'mouse-wheel x) + (< (car (cdr (cdr event))) 0) ;; Emacs 21.3 + ;; Emacs > 21.3 + (eq x tabbar--mwheel-up-event)))) + +;;; Basic commands +;; +;;;###autoload +(defun tabbar-mwheel-backward (event) + "Select the previous available tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-backward'." + (interactive "@e") + (tabbar-cycle t event)) + +;;;###autoload +(defun tabbar-mwheel-forward (event) + "Select the next available tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-forward'." + (interactive "@e") + (tabbar-cycle nil event)) + +;;;###autoload +(defun tabbar-mwheel-backward-group (event) + "Go to selected tab in the previous available group. +If there is only one group, select the previous visible tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-backward-group'." + (interactive "@e") + (let ((tabbar-cycle-scope 'groups)) + (tabbar-cycle t event))) + +;;;###autoload +(defun tabbar-mwheel-forward-group (event) + "Go to selected tab in the next available group. +If there is only one group, select the next visible tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-forward-group'." + (interactive "@e") + (let ((tabbar-cycle-scope 'groups)) + (tabbar-cycle nil event))) + +;;;###autoload +(defun tabbar-mwheel-backward-tab (event) + "Select the previous visible tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-backward-tab'." + (interactive "@e") + (let ((tabbar-cycle-scope 'tabs)) + (tabbar-cycle t event))) + +;;;###autoload +(defun tabbar-mwheel-forward-tab (event) + "Select the next visible tab. +EVENT is the mouse event that triggered this command. +Mouse-enabled equivalent of the command `tabbar-forward-tab'." + (interactive "@e") + (let ((tabbar-cycle-scope 'tabs)) + (tabbar-cycle nil event))) + +;;; Wrappers when there is only one generic mouse-wheel event +;; +;;;###autoload +(defun tabbar-mwheel-switch-tab (event) + "Select the next or previous tab according to EVENT." + (interactive "@e") + (if (tabbar--mwheel-up-p event) + (tabbar-mwheel-forward-tab event) + (tabbar-mwheel-backward-tab event))) + +;;;###autoload +(defun tabbar-mwheel-switch-group (event) + "Select the next or previous group of tabs according to EVENT." + (interactive "@e") + (if (tabbar--mwheel-up-p event) + (tabbar-mwheel-forward-group event) + (tabbar-mwheel-backward-group event))) + +;;; Minor modes +;; +(defsubst tabbar-mode-on-p () + "Return non-nil if Tabbar mode is on." + (eq (default-value 'header-line-format) + tabbar-header-line-format)) + +;;; Tabbar-Local mode +;; +(defvar tabbar--local-hlf nil) + +;;;###autoload +(define-minor-mode tabbar-local-mode + "Toggle local display of the tab bar. +With prefix argument ARG, turn on if positive, otherwise off. +Returns non-nil if the new state is enabled. +When turned on, if a local header line is shown, it is hidden to show +the tab bar. The tab bar is locally hidden otherwise. When turned +off, if a local header line is hidden or the tab bar is locally +hidden, it is shown again. Signal an error if Tabbar mode is off." + :group 'tabbar + :global nil + (unless (tabbar-mode-on-p) + (error "Tabbar mode must be enabled")) +;;; ON + (if tabbar-local-mode + (if (and (local-variable-p 'header-line-format) + header-line-format) + ;; A local header line exists, hide it to show the tab bar. + (progn + ;; Fail in case of an inconsistency because another local + ;; header line is already hidden. + (when (local-variable-p 'tabbar--local-hlf) + (error "Another local header line is already hidden")) + (set (make-local-variable 'tabbar--local-hlf) + header-line-format) + (kill-local-variable 'header-line-format)) + ;; Otherwise hide the tab bar in this buffer. + (setq header-line-format nil)) +;;; OFF + (if (local-variable-p 'tabbar--local-hlf) + ;; A local header line is hidden, show it again. + (progn + (setq header-line-format tabbar--local-hlf) + (kill-local-variable 'tabbar--local-hlf)) + ;; The tab bar is locally hidden, show it again. + (kill-local-variable 'header-line-format)))) + +;;; Tabbar mode +;; +(defvar tabbar-prefix-key [(control ?c)] + "The common prefix key used in Tabbar mode.") + +(defvar tabbar-prefix-map + (let ((km (make-sparse-keymap))) + (define-key km [(control home)] 'tabbar-press-home) + (define-key km [(control left)] 'tabbar-backward) + (define-key km [(control right)] 'tabbar-forward) + (define-key km [(control up)] 'tabbar-backward-group) + (define-key km [(control down)] 'tabbar-forward-group) + (define-key km [(control prior)] 'tabbar-press-scroll-left) + (define-key km [(control next)] 'tabbar-press-scroll-right) + (define-key km [(control f10)] 'tabbar-local-mode) + km) + "The key bindings provided in Tabbar mode.") + +(defvar tabbar-mode-map + (let ((km (make-sparse-keymap))) + (define-key km tabbar-prefix-key tabbar-prefix-map) + km) + "Keymap to use in Tabbar mode.") + +(defvar tabbar--global-hlf nil) + +;;;###autoload +(define-minor-mode tabbar-mode + "Toggle display of a tab bar in the header line. +With prefix argument ARG, turn on if positive, otherwise off. +Returns non-nil if the new state is enabled. + +\\{tabbar-mode-map}" + :group 'tabbar + :require 'tabbar + :global t + :keymap tabbar-mode-map + (if tabbar-mode +;;; ON + (unless (tabbar-mode-on-p) + ;; Save current default value of `header-line-format'. + (setq tabbar--global-hlf (default-value 'header-line-format)) + (tabbar-init-tabsets-store) + (setq-default header-line-format tabbar-header-line-format)) +;;; OFF + (when (tabbar-mode-on-p) + ;; Turn off Tabbar-Local mode globally. + (mapc #'(lambda (b) + (condition-case nil + (with-current-buffer b + (and tabbar-local-mode + (tabbar-local-mode -1))) + (error nil))) + (buffer-list)) + ;; Restore previous `header-line-format'. + (setq-default header-line-format tabbar--global-hlf) + (tabbar-free-tabsets-store)) + )) + +;;; Tabbar-Mwheel mode +;; +(defvar tabbar-mwheel-mode-map + (let ((km (make-sparse-keymap))) + (if (get 'mouse-wheel 'event-symbol-elements) + ;; Use one generic mouse wheel event + (define-key km [A-mouse-wheel] + 'tabbar-mwheel-switch-group) + ;; Use separate up/down mouse wheel events + (let ((up (tabbar--mwheel-key tabbar--mwheel-up-event)) + (down (tabbar--mwheel-key tabbar--mwheel-down-event))) + (define-key km `[header-line ,down] + 'tabbar-mwheel-backward-group) + (define-key km `[header-line ,up] + 'tabbar-mwheel-forward-group) + (define-key km `[header-line (control ,down)] + 'tabbar-mwheel-backward-tab) + (define-key km `[header-line (control ,up)] + 'tabbar-mwheel-forward-tab) + (define-key km `[header-line (shift ,down)] + 'tabbar-mwheel-backward) + (define-key km `[header-line (shift ,up)] + 'tabbar-mwheel-forward) + )) + km) + "Keymap to use in Tabbar-Mwheel mode.") + +;;;###autoload +(define-minor-mode tabbar-mwheel-mode + "Toggle use of the mouse wheel to navigate through tabs or groups. +With prefix argument ARG, turn on if positive, otherwise off. +Returns non-nil if the new state is enabled. + +\\{tabbar-mwheel-mode-map}" + :group 'tabbar + :require 'tabbar + :global t + :keymap tabbar-mwheel-mode-map + (when tabbar-mwheel-mode + (unless (and mouse-wheel-mode tabbar-mode) + (tabbar-mwheel-mode -1)))) + +(defun tabbar-mwheel-follow () + "Toggle Tabbar-Mwheel following Tabbar and Mouse-Wheel modes." + (tabbar-mwheel-mode (if (and mouse-wheel-mode tabbar-mode) 1 -1))) + +(add-hook 'tabbar-mode-hook 'tabbar-mwheel-follow) +(add-hook 'mouse-wheel-mode-hook 'tabbar-mwheel-follow) + +;;; Buffer tabs +;; +(defgroup tabbar-buffer nil + "Display buffers in the tab bar." + :group 'tabbar) + +(defcustom tabbar-buffer-home-button + (cons (cons "[+]" tabbar-home-button-enabled-image) + (cons "[-]" tabbar-home-button-disabled-image)) + "The home button displayed when showing buffer tabs. +The enabled button value is displayed when showing tabs for groups of +buffers, and the disabled button value is displayed when showing +buffer tabs. +The variable `tabbar-button-widget' gives details on this widget." + :group 'tabbar-buffer + :type tabbar-button-widget + :set '(lambda (variable value) + (custom-set-default variable value) + ;; Schedule refresh of button value. + (setq tabbar-home-button-value nil))) + +(defvar tabbar-buffer-list-function 'tabbar-buffer-list + "Function that returns the list of buffers to show in tabs. +That function is called with no arguments and must return a list of +buffers.") + +(defvar tabbar-buffer-groups-function 'tabbar-buffer-groups + "Function that gives the group names the current buffer belongs to. +It must return a list of group names, or nil if the buffer has no +group. Notice that it is better that a buffer belongs to one group.") + +(defun tabbar-buffer-list () + "Return the list of buffers to show in tabs. +Exclude buffers whose name starts with a space, when they are not +visiting a file. The current buffer is always included." + (delq nil + (mapcar #'(lambda (b) + (cond + ;; Always include the current buffer. + ((eq (current-buffer) b) b) + ((buffer-file-name b) b) + ((char-equal ?\ (aref (buffer-name b) 0)) nil) + ((buffer-live-p b) b))) + (buffer-list)))) + +(defun tabbar-buffer-mode-derived-p (mode parents) + "Return non-nil if MODE derives from a mode in PARENTS." + (let (derived) + (while (and (not derived) mode) + (if (memq mode parents) + (setq derived t) + (setq mode (get mode 'derived-mode-parent)))) + derived)) + +(defun tabbar-buffer-groups () + "Return the list of group names the current buffer belongs to. +Return a list of one element based on major mode." + (list + (cond + ((or (get-buffer-process (current-buffer)) + ;; Check if the major mode derives from `comint-mode' or + ;; `compilation-mode'. + (tabbar-buffer-mode-derived-p + major-mode '(comint-mode compilation-mode))) + "Process" + ) + ((member (buffer-name) + '("*scratch*" "*Messages*")) + "Common" + ) + ((eq major-mode 'dired-mode) + "Dired" + ) + ((memq major-mode + '(help-mode apropos-mode Info-mode Man-mode)) + "Help" + ) + ((memq major-mode + '(rmail-mode + rmail-edit-mode vm-summary-mode vm-mode mail-mode + mh-letter-mode mh-show-mode mh-folder-mode + gnus-summary-mode message-mode gnus-group-mode + gnus-article-mode score-mode gnus-browse-killed-mode)) + "Mail" + ) + (t + ;; Return `mode-name' if not blank, `major-mode' otherwise. + (if (and (stringp mode-name) + ;; Take care of preserving the match-data because this + ;; function is called when updating the header line. + (save-match-data (string-match "[^ ]" mode-name))) + mode-name + (symbol-name major-mode)) + )))) + +;;; Group buffers in tab sets. +;; +(defvar tabbar--buffers nil) + +(defun tabbar-buffer-update-groups () + "Update tab sets from groups of existing buffers. +Return the the first group where the current buffer is." + (let ((bl (sort + (mapcar + #'(lambda (b) + (with-current-buffer b + (list (current-buffer) + (buffer-name) + (if tabbar-buffer-groups-function + (funcall tabbar-buffer-groups-function) + '("Common"))))) + (and tabbar-buffer-list-function + (funcall tabbar-buffer-list-function))) + #'(lambda (e1 e2) + (string-lessp (nth 1 e1) (nth 1 e2)))))) + ;; If the cache has changed, update the tab sets. + (unless (equal bl tabbar--buffers) + ;; Add new buffers, or update changed ones. + (dolist (e bl) + (dolist (g (nth 2 e)) + (let ((tabset (tabbar-get-tabset g))) + (if tabset + (unless (equal e (assq (car e) tabbar--buffers)) + ;; This is a new buffer, or a previously existing + ;; buffer that has been renamed, or moved to another + ;; group. Update the tab set, and the display. + (tabbar-add-tab tabset (car e) t) + (tabbar-set-template tabset nil)) + (tabbar-make-tabset g (car e)))))) + ;; Remove tabs for buffers not found in cache or moved to other + ;; groups, and remove empty tabsets. + (mapc 'tabbar-delete-tabset + (tabbar-map-tabsets + #'(lambda (tabset) + (dolist (tab (tabbar-tabs tabset)) + (let ((e (assq (tabbar-tab-value tab) bl))) + (or (and e (memq tabset + (mapcar 'tabbar-get-tabset + (nth 2 e)))) + (tabbar-delete-tab tab)))) + ;; Return empty tab sets + (unless (tabbar-tabs tabset) + tabset)))) + ;; The new cache becomes the current one. + (setq tabbar--buffers bl))) + ;; Return the first group the current buffer belongs to. + (car (nth 2 (assq (current-buffer) tabbar--buffers)))) + +;;; Tab bar callbacks +;; +(defvar tabbar--buffer-show-groups nil) + +(defsubst tabbar-buffer-show-groups (flag) + "Set display of tabs for groups of buffers to FLAG." + (setq tabbar--buffer-show-groups flag + ;; Redisplay the home button. + tabbar-home-button-value nil)) + +(defun tabbar-buffer-tabs () + "Return the buffers to display on the tab bar, in a tab set." + (let ((tabset (tabbar-get-tabset (tabbar-buffer-update-groups)))) + (tabbar-select-tab-value (current-buffer) tabset) + (when tabbar--buffer-show-groups + (setq tabset (tabbar-get-tabsets-tabset)) + (tabbar-select-tab-value (current-buffer) tabset)) + tabset)) + +(defun tabbar-buffer-button-label (name) + "Return a label for button NAME. +That is a pair (ENABLED . DISABLED), where ENABLED and DISABLED are +respectively the appearance of the button when enabled and disabled. +They are propertized strings which could display images, as specified +by the variable `tabbar-button-label'. +When NAME is 'home, return a different ENABLED button if showing tabs +or groups. Call the function `tabbar-button-label' otherwise." + (let ((lab (tabbar-button-label name))) + (when (eq name 'home) + (let* ((btn tabbar-buffer-home-button) + (on (tabbar-find-image (cdar btn))) + (off (tabbar-find-image (cddr btn)))) + ;; When `tabbar-buffer-home-button' does not provide a value, + ;; default to the enabled value of `tabbar-home-button'. + (if on + (tabbar-normalize-image on 1) + (setq on (get-text-property 0 'display (car lab)))) + (if off + (tabbar-normalize-image off 1) + (setq off (get-text-property 0 'display (car lab)))) + (setcar lab + (if tabbar--buffer-show-groups + (propertize (or (caar btn) (car lab)) 'display on) + (propertize (or (cadr btn) (car lab)) 'display off))) + )) + lab)) + +(defun tabbar-buffer-tab-label (tab) + "Return a label for TAB. +That is, a string used to represent it on the tab bar." + (let ((label (if tabbar--buffer-show-groups + (format "[%s]" (tabbar-tab-tabset tab)) + (format "%s" (tabbar-tab-value tab))))) + ;; Unless the tab bar auto scrolls to keep the selected tab + ;; visible, shorten the tab label to keep as many tabs as possible + ;; in the visible area of the tab bar. + (if tabbar-auto-scroll-flag + label + (tabbar-shorten + label (max 1 (/ (window-width) + (length (tabbar-view + (tabbar-current-tabset))))))))) + +(defun tabbar-buffer-help-on-tab (tab) + "Return the help string shown when mouse is onto TAB." + (if tabbar--buffer-show-groups + (let* ((tabset (tabbar-tab-tabset tab)) + (tab (tabbar-selected-tab tabset))) + (format "mouse-1: switch to buffer %S in group [%s]" + (buffer-name (tabbar-tab-value tab)) tabset)) + (format "mouse-1: switch to buffer %S\n\ +mouse-2: pop to buffer, mouse-3: delete other windows" + (buffer-name (tabbar-tab-value tab))) + )) + +(defun tabbar-buffer-select-tab (event tab) + "On mouse EVENT, select TAB." + (let ((mouse-button (event-basic-type event)) + (buffer (tabbar-tab-value tab))) + (cond + ((eq mouse-button 'mouse-2) + (pop-to-buffer buffer t)) + ((eq mouse-button 'mouse-3) + (delete-other-windows)) + (t + (switch-to-buffer buffer))) + ;; Don't show groups. + (tabbar-buffer-show-groups nil) + )) + +(defun tabbar-buffer-click-on-home (event) + "Handle a mouse click EVENT on the tab bar home button. +mouse-1, toggle the display of tabs for groups of buffers. +mouse-3, close the current buffer." + (let ((mouse-button (event-basic-type event))) + (cond + ((eq mouse-button 'mouse-1) + (tabbar-buffer-show-groups (not tabbar--buffer-show-groups))) + ((eq mouse-button 'mouse-3) + (kill-buffer nil)) + ))) + +(defun tabbar-buffer-help-on-home () + "Return the help string shown when mouse is onto the toggle button." + (concat + (if tabbar--buffer-show-groups + "mouse-1: show buffers in selected group" + "mouse-1: show groups of buffers") + ", mouse-3: close current buffer")) + +(defun tabbar-buffer-track-killed () + "Hook run just before actually killing a buffer. +In Tabbar mode, try to switch to a buffer in the current tab bar, +after the current buffer has been killed. Try first the buffer in tab +after the current one, then the buffer in tab before. On success, put +the sibling buffer in front of the buffer list, so it will be selected +first." + (and (eq header-line-format tabbar-header-line-format) + (eq tabbar-current-tabset-function 'tabbar-buffer-tabs) + (eq (current-buffer) (window-buffer (selected-window))) + (let ((bl (tabbar-tab-values (tabbar-current-tabset))) + (b (current-buffer)) + found sibling) + (while (and bl (not found)) + (if (eq b (car bl)) + (setq found t) + (setq sibling (car bl))) + (setq bl (cdr bl))) + (when (and (setq sibling (or (car bl) sibling)) + (buffer-live-p sibling)) + ;; Move sibling buffer in front of the buffer list. + (save-current-buffer + (switch-to-buffer sibling)))))) + +;;; Tab bar buffer setup +;; +(defun tabbar-buffer-init () + "Initialize tab bar buffer data. +Run as `tabbar-init-hook'." + (setq tabbar--buffers nil + tabbar--buffer-show-groups nil + tabbar-current-tabset-function 'tabbar-buffer-tabs + tabbar-tab-label-function 'tabbar-buffer-tab-label + tabbar-select-tab-function 'tabbar-buffer-select-tab + tabbar-help-on-tab-function 'tabbar-buffer-help-on-tab + tabbar-button-label-function 'tabbar-buffer-button-label + tabbar-home-function 'tabbar-buffer-click-on-home + tabbar-home-help-function 'tabbar-buffer-help-on-home + ) + (add-hook 'kill-buffer-hook 'tabbar-buffer-track-killed)) + +(defun tabbar-buffer-quit () + "Quit tab bar buffer. +Run as `tabbar-quit-hook'." + (setq tabbar--buffers nil + tabbar--buffer-show-groups nil + tabbar-current-tabset-function nil + tabbar-tab-label-function nil + tabbar-select-tab-function nil + tabbar-help-on-tab-function nil + tabbar-button-label-function nil + tabbar-home-function nil + tabbar-home-help-function nil + ) + (remove-hook 'kill-buffer-hook 'tabbar-buffer-track-killed)) + +(add-hook 'tabbar-init-hook 'tabbar-buffer-init) +(add-hook 'tabbar-quit-hook 'tabbar-buffer-quit) + +(provide 'tabbar) + +(run-hooks 'tabbar-load-hook) + +;;; tabbar.el ends here