From d8c80081f67fd7674a0a7391b82fe0431ddb6552 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Thu, 17 May 2012 00:01:30 -0300 Subject: [PATCH] Deleted lisp/progmodes/python.el as first step of the new python.el merge. --- lisp/progmodes/python.el | 2730 ---------------------------------------------- 1 file changed, 2730 deletions(-) delete mode 100644 lisp/progmodes/python.el diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el deleted file mode 100644 index 620ea8cd519..00000000000 --- a/lisp/progmodes/python.el +++ /dev/null @@ -1,2730 +0,0 @@ -;;; python.el --- silly walks for Python -*- coding: iso-8859-1 -*- - -;; Copyright (C) 2003-2012 Free Software Foundation, Inc. - -;; Author: Dave Love -;; Maintainer: FSF -;; Created: Nov 2003 -;; Keywords: languages - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; Major mode for editing Python, with support for inferior processes. - -;; There is another Python mode, python-mode.el: -;; http://launchpad.net/python-mode -;; used by XEmacs, and originally maintained with Python. -;; That isn't covered by an FSF copyright assignment (?), unlike this -;; code, and seems not to be well-maintained for Emacs (though I've -;; submitted fixes). This mode is rather simpler and is better in -;; other ways. In particular, using the syntax functions with text -;; properties maintained by font-lock makes it more correct with -;; arbitrary string and comment contents. - -;; This doesn't implement all the facilities of python-mode.el. Some -;; just need doing, e.g. catching exceptions in the inferior Python -;; buffer (but see M-x pdb for debugging). [Actually, the use of -;; `compilation-shell-minor-mode' now is probably enough for that.] -;; Others don't seem appropriate. For instance, -;; `forward-into-nomenclature' should be done separately, since it's -;; not specific to Python, and I've installed a minor mode to do the -;; job properly in Emacs 23. [CC mode 5.31 contains an incompatible -;; feature, `subword-mode' which is intended to have a similar -;; effect, but actually only affects word-oriented keybindings.] - -;; Other things seem more natural or canonical here, e.g. the -;; {beginning,end}-of-defun implementation dealing with nested -;; definitions, and the inferior mode following `cmuscheme'. (The -;; inferior mode can find the source of errors from -;; `python-send-region' & al via `compilation-shell-minor-mode'.) -;; There is (limited) symbol completion using lookup in Python and -;; Eldoc support also using the inferior process. Successive TABs -;; cycle between possible indentations for the line. - -;; Even where it has similar facilities, this mode is incompatible -;; with python-mode.el in some respects. For instance, various key -;; bindings are changed to obey Emacs conventions. - -;; TODO: See various Fixmes below. - -;; Fixme: This doesn't support (the nascent) Python 3 . - -;;; Code: - -(require 'comint) -(require 'ansi-color) - -(eval-when-compile (require 'compile)) - -(defgroup python nil - "Silly walks in the Python language." - :group 'languages - :version "22.1" - :link '(emacs-commentary-link "python")) - -;;;###autoload -(add-to-list 'interpreter-mode-alist (cons (purecopy "jython") 'jython-mode)) -;;;###autoload -(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode)) -;;;###autoload -(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'") 'python-mode)) - -;;;; Font lock - -(defvar python-font-lock-keywords - `(,(rx symbol-start - ;; From v 2.7 reference, § keywords. - ;; def and class dealt with separately below - (or "and" "as" "assert" "break" "continue" "del" "elif" "else" - "except" "exec" "finally" "for" "from" "global" "if" - "import" "in" "is" "lambda" "not" "or" "pass" "print" - "raise" "return" "try" "while" "with" "yield" - ;; Not real keywords, but close enough to be fontified as such - "self" "True" "False" - ;; Python 3 - "nonlocal") - symbol-end) - (,(rx symbol-start "None" symbol-end) ; see § Keywords in 2.7 manual - . font-lock-constant-face) - ;; Definitions - (,(rx symbol-start (group "class") (1+ space) (group (1+ (or word ?_)))) - (1 font-lock-keyword-face) (2 font-lock-type-face)) - (,(rx symbol-start (group "def") (1+ space) (group (1+ (or word ?_)))) - (1 font-lock-keyword-face) (2 font-lock-function-name-face)) - ;; Top-level assignments are worth highlighting. - (,(rx line-start (group (1+ (or word ?_))) (0+ space) - (opt (or "+" "-" "*" "**" "/" "//" "&" "%" "|" "^" "<<" ">>")) "=") - (1 font-lock-variable-name-face)) - ;; Decorators. - (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) - (0+ "." (1+ (or word ?_))))) - (1 font-lock-type-face)) - ;; Built-ins. (The next three blocks are from - ;; `__builtin__.__dict__.keys()' in Python 2.7) These patterns - ;; are debatable, but they at least help to spot possible - ;; shadowing of builtins. - (,(rx symbol-start (or - ;; exceptions - "ArithmeticError" "AssertionError" "AttributeError" - "BaseException" "DeprecationWarning" "EOFError" - "EnvironmentError" "Exception" "FloatingPointError" - "FutureWarning" "GeneratorExit" "IOError" "ImportError" - "ImportWarning" "IndentationError" "IndexError" "KeyError" - "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" - "NotImplemented" "NotImplementedError" "OSError" - "OverflowError" "PendingDeprecationWarning" "ReferenceError" - "RuntimeError" "RuntimeWarning" "StandardError" - "StopIteration" "SyntaxError" "SyntaxWarning" "SystemError" - "SystemExit" "TabError" "TypeError" "UnboundLocalError" - "UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError" - "UnicodeTranslateError" "UnicodeWarning" "UserWarning" - "ValueError" "Warning" "ZeroDivisionError" - ;; Python 2.7 - "BufferError" "BytesWarning" "WindowsError") symbol-end) - . font-lock-type-face) - (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start - (group (or - ;; callable built-ins, fontified when not appearing as - ;; object attributes - "abs" "all" "any" "apply" "basestring" "bool" "buffer" "callable" - "chr" "classmethod" "cmp" "coerce" "compile" "complex" - "copyright" "credits" "delattr" "dict" "dir" "divmod" - "enumerate" "eval" "execfile" "exit" "file" "filter" "float" - "frozenset" "getattr" "globals" "hasattr" "hash" "help" - "hex" "id" "input" "int" "intern" "isinstance" "issubclass" - "iter" "len" "license" "list" "locals" "long" "map" "max" - "min" "object" "oct" "open" "ord" "pow" "property" "quit" - "range" "raw_input" "reduce" "reload" "repr" "reversed" - "round" "set" "setattr" "slice" "sorted" "staticmethod" - "str" "sum" "super" "tuple" "type" "unichr" "unicode" "vars" - "xrange" "zip" - ;; Python 2.7. - "bin" "bytearray" "bytes" "format" "memoryview" "next" "print" - )) symbol-end) - (1 font-lock-builtin-face)) - (,(rx symbol-start (or - ;; other built-ins - "True" "False" "None" "Ellipsis" - "_" "__debug__" "__doc__" "__import__" "__name__" "__package__") - symbol-end) - . font-lock-builtin-face))) - -(defconst python-syntax-propertize-function - ;; 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. - (syntax-propertize-rules - (;; ¡Backrefs don't work in syntax-propertize-rules! - (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. - "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") - (3 (ignore (python-quote-syntax)))) - ;; This doesn't really help. - ;;((rx (and ?\\ (group ?\n))) (1 " ")) - )) - -(defun python-quote-syntax () - "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)) - (let ((syntax (save-match-data (syntax-ppss)))) - (cond - ((eq t (nth 3 syntax)) ; after unclosed fence - ;; Consider property for the last char if in a fenced string. - (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))) - (put-text-property (match-beginning 3) (match-end 3) - 'syntax-table (string-to-syntax "|")))) - ((match-end 1) - ;; Consider property for initial char, accounting for prefixes. - (put-text-property (match-beginning 1) (match-end 1) - 'syntax-table (string-to-syntax "|"))) - (t - ;; Consider property for initial char, accounting for prefixes. - (put-text-property (match-beginning 2) (match-end 2) - 'syntax-table (string-to-syntax "|")))) - ))) - -;; This isn't currently in `font-lock-defaults' as probably not worth -;; it -- we basically only mess with a few normally-symbol characters. - -;; (defun python-font-lock-syntactic-face-function (state) -;; "`font-lock-syntactic-face-function' for Python mode. -;; Returns the string or comment face as usual, with side effect of putting -;; a `syntax-table' property on the inside of the string or comment which is -;; the standard syntax table." -;; (if (nth 3 state) -;; (save-excursion -;; (goto-char (nth 8 state)) -;; (condition-case nil -;; (forward-sexp) -;; (error nil)) -;; (put-text-property (1+ (nth 8 state)) (1- (point)) -;; 'syntax-table (standard-syntax-table)) -;; 'font-lock-string-face) -;; (put-text-property (1+ (nth 8 state)) (line-end-position) -;; 'syntax-table (standard-syntax-table)) -;; 'font-lock-comment-face)) - -;;;; Keymap and syntax - -(defvar python-mode-map - (let ((map (make-sparse-keymap))) - ;; Mostly taken from python-mode.el. - (define-key map ":" 'python-electric-colon) - (define-key map "\177" 'python-backspace) - (define-key map "\C-c<" 'python-shift-left) - (define-key map "\C-c>" 'python-shift-right) - (define-key map "\C-c\C-k" 'python-mark-block) - (define-key map "\C-c\C-d" 'python-pdbtrack-toggle-stack-tracking) - (define-key map "\C-c\C-n" 'python-next-statement) - (define-key map "\C-c\C-p" 'python-previous-statement) - (define-key map "\C-c\C-u" 'python-beginning-of-block) - (define-key map "\C-c\C-f" 'python-describe-symbol) - (define-key map "\C-c\C-w" 'python-check) - (define-key map "\C-c\C-v" 'python-check) ; a la sgml-mode - (define-key map "\C-c\C-s" 'python-send-string) - (define-key map [?\C-\M-x] 'python-send-defun) - (define-key map "\C-c\C-r" 'python-send-region) - (define-key map "\C-c\M-r" 'python-send-region-and-go) - (define-key map "\C-c\C-c" 'python-send-buffer) - (define-key map "\C-c\C-z" 'python-switch-to-python) - (define-key map "\C-c\C-m" 'python-load-file) - (define-key map "\C-c\C-l" 'python-load-file) ; a la cmuscheme - (substitute-key-definition 'complete-symbol 'completion-at-point - map global-map) - (define-key map "\C-c\C-i" 'python-find-imports) - (define-key map "\C-c\C-t" 'python-expand-template) - (easy-menu-define python-menu map "Python Mode menu" - `("Python" - :help "Python-specific Features" - ["Shift region left" python-shift-left :active mark-active - :help "Shift by a single indentation step"] - ["Shift region right" python-shift-right :active mark-active - :help "Shift by a single indentation step"] - "-" - ["Mark block" python-mark-block - :help "Mark innermost block around point"] - ["Mark def/class" mark-defun - :help "Mark innermost definition around point"] - "-" - ["Start of block" python-beginning-of-block - :help "Go to start of innermost definition around point"] - ["End of block" python-end-of-block - :help "Go to end of innermost definition around point"] - ["Start of def/class" beginning-of-defun - :help "Go to start of innermost definition around point"] - ["End of def/class" end-of-defun - :help "Go to end of innermost definition around point"] - "-" - ("Templates..." - :help "Expand templates for compound statements" - :filter (lambda (&rest junk) - (abbrev-table-menu python-mode-abbrev-table))) - "-" - ["Start interpreter" run-python - :help "Run `inferior' Python in separate buffer"] - ["Import/reload file" python-load-file - :help "Load into inferior Python session"] - ["Eval buffer" python-send-buffer - :help "Evaluate buffer en bloc in inferior Python session"] - ["Eval region" python-send-region :active mark-active - :help "Evaluate region en bloc in inferior Python session"] - ["Eval def/class" python-send-defun - :help "Evaluate current definition in inferior Python session"] - ["Switch to interpreter" python-switch-to-python - :help "Switch to inferior Python buffer"] - ["Set default process" python-set-proc - :help "Make buffer's inferior process the default" - :active (buffer-live-p python-buffer)] - ["Check file" python-check :help "Run pychecker"] - ["Debugger" pdb :help "Run pdb under GUD"] - "-" - ["Help on symbol" python-describe-symbol - :help "Use pydoc on symbol at point"] - ["Complete symbol" completion-at-point - :help "Complete (qualified) symbol before point"] - ["Find function" python-find-function - :help "Try to find source definition of function at point"] - ["Update imports" python-find-imports - :help "Update list of top-level imports for completion"])) - map)) -;; Fixme: add toolbar stuff for useful things like symbol help, send -;; region, at least. (Shouldn't be specific to Python, obviously.) -;; eric has items including: (un)indent, (un)comment, restart script, -;; run script, debug script; also things for profiling, unit testing. - -(defvar 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) - table)) - -;;;; Utility stuff - -(defsubst python-in-string/comment () - "Return non-nil if point is in a Python literal (a comment or string)." - ;; We don't need to save the match data. - (nth 8 (syntax-ppss))) - -(defconst python-space-backslash-table - (let ((table (copy-syntax-table python-mode-syntax-table))) - (modify-syntax-entry ?\\ " " table) - table) - "`python-mode-syntax-table' with backslash given whitespace syntax.") - -(defun python-skip-comments/blanks (&optional backward) - "Skip comments and blank lines. -BACKWARD non-nil means go backwards, otherwise go forwards. -Backslash is treated as whitespace so that continued blank lines -are skipped. Doesn't move out of comments -- should be outside -or at end of line." - (let ((arg (if backward - ;; If we're in a comment (including on the trailing - ;; newline), forward-comment doesn't move backwards out - ;; of it. Don't set the syntax table round this bit! - (let ((syntax (syntax-ppss))) - (if (nth 4 syntax) - (goto-char (nth 8 syntax))) - (- (point-max))) - (point-max)))) - (with-syntax-table python-space-backslash-table - (forward-comment arg)))) - -(defun python-backslash-continuation-line-p () - "Non-nil if preceding line ends with backslash that is not in a comment." - (and (eq ?\\ (char-before (line-end-position 0))) - (not (syntax-ppss-context (syntax-ppss))))) - -(defun python-continuation-line-p () - "Return non-nil if current line continues a previous one. -The criteria are that the previous line ends in a backslash outside -comments and strings, or that point is within brackets/parens." - (or (python-backslash-continuation-line-p) - (let ((depth (syntax-ppss-depth - (save-excursion ; syntax-ppss with arg changes point - (syntax-ppss (line-beginning-position)))))) - (or (> depth 0) - (if (< depth 0) ; Unbalanced brackets -- act locally - (save-excursion - (condition-case () - (progn (backward-up-list) t) ; actually within brackets - (error nil)))))))) - -(defun python-comment-line-p () - "Return non-nil if and only if current line has only a comment." - (save-excursion - (end-of-line) - (when (eq 'comment (syntax-ppss-context (syntax-ppss))) - (back-to-indentation) - (looking-at (rx (or (syntax comment-start) line-end)))))) - -(defun python-blank-line-p () - "Return non-nil if and only if current line is blank." - (save-excursion - (beginning-of-line) - (looking-at "\\s-*$"))) - -(defun python-beginning-of-string () - "Go to beginning of string around point. -Do nothing if not in string." - (let ((state (syntax-ppss))) - (when (eq 'string (syntax-ppss-context state)) - (goto-char (nth 8 state))))) - -(defun python-open-block-statement-p (&optional bos) - "Return non-nil if statement at point opens a block. -BOS non-nil means point is known to be at beginning of statement." - (save-excursion - (unless bos (python-beginning-of-statement)) - (looking-at (rx (and (or "if" "else" "elif" "while" "for" "def" - "class" "try" "except" "finally" "with") - symbol-end))))) - -(defun python-close-block-statement-p (&optional bos) - "Return non-nil if current line is a statement closing a block. -BOS non-nil means point is at beginning of statement. -The criteria are that the line isn't a comment or in string and - starts with keyword `raise', `break', `continue' or `pass'." - (save-excursion - (unless bos (python-beginning-of-statement)) - (back-to-indentation) - (looking-at (rx (or "return" "raise" "break" "continue" "pass") - symbol-end)))) - -(defun python-outdent-p () - "Return non-nil if current line should outdent a level." - (save-excursion - (back-to-indentation) - (and (looking-at (rx (and (or "else" "finally" "except" "elif") - symbol-end))) - (not (python-in-string/comment)) - ;; Ensure there's a previous statement and move to it. - (zerop (python-previous-statement)) - (not (python-close-block-statement-p t)) - ;; Fixme: check this - (not (python-open-block-statement-p))))) - -;;;; Indentation. - -(defcustom python-indent 4 - "Number of columns for a unit of indentation in Python mode. -See also `\\[python-guess-indent]'" - :group 'python - :type 'integer) -(put 'python-indent 'safe-local-variable 'integerp) - -(defcustom python-guess-indent t - "Non-nil means Python mode guesses `python-indent' for the buffer." - :type 'boolean - :group 'python) - -(defcustom python-indent-string-contents t - "Non-nil means indent contents of multi-line strings together. -This means indent them the same as the preceding non-blank line. -Otherwise preserve their indentation. - -This only applies to `doc' strings, i.e. those that form statements; -the indentation is preserved in others." - :type '(choice (const :tag "Align with preceding" t) - (const :tag "Preserve indentation" nil)) - :group 'python) - -(defcustom python-honour-comment-indentation nil - "Non-nil means indent relative to preceding comment line. -Only do this for comments where the leading comment character is -followed by space. This doesn't apply to comment lines, which -are always indented in lines with preceding comments." - :type 'boolean - :group 'python) - -(defcustom python-continuation-offset 4 - "Number of columns of additional indentation for continuation lines. -Continuation lines follow a backslash-terminated line starting a -statement." - :group 'python - :type 'integer) - - -(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) -(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) - -;; 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)) - -(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 - :version "24.1") - -(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 - :version "24.1") - -(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.") - -(defun python-guess-indent () - "Guess step for indentation of current buffer. -Set `python-indent' locally to the value guessed." - (interactive) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (let (done indent) - (while (and (not done) (not (eobp))) - (when (and (re-search-forward (rx ?: (0+ space) - (or (syntax comment-start) - line-end)) - nil 'move) - (python-open-block-statement-p)) - (save-excursion - (python-beginning-of-statement) - (let ((initial (current-indentation))) - (if (zerop (python-next-statement)) - (setq indent (- (current-indentation) initial))) - (if (and indent (>= indent 2) (<= indent 8)) ; sanity check - (setq done t)))))) - (when done - (when (/= indent (default-value 'python-indent)) - (set (make-local-variable 'python-indent) indent) - (unless (= tab-width python-indent) - (setq indent-tabs-mode nil))) - indent))))) - -;; Alist of possible indentations and start of statement they would -;; close. Used in indentation cycling (below). -(defvar python-indent-list nil - "Internal use.") -;; Length of the above -(defvar python-indent-list-length nil - "Internal use.") -;; Current index into the alist. -(defvar python-indent-index nil - "Internal use.") - -(defun python-calculate-indentation () - "Calculate Python indentation for line at point." - (setq python-indent-list nil - python-indent-list-length 1) - (save-excursion - (beginning-of-line) - (let ((syntax (syntax-ppss)) - start) - (cond - ((eq 'string (syntax-ppss-context syntax)) ; multi-line string - (if (not python-indent-string-contents) - (current-indentation) - ;; Only respect `python-indent-string-contents' in doc - ;; strings (defined as those which form statements). - (if (not (save-excursion - (python-beginning-of-statement) - (looking-at (rx (or (syntax string-delimiter) - (syntax string-quote)))))) - (current-indentation) - ;; Find indentation of preceding non-blank line within string. - (setq start (nth 8 syntax)) - (forward-line -1) - (while (and (< start (point)) (looking-at "\\s-*$")) - (forward-line -1)) - (current-indentation)))) - ((python-continuation-line-p) ; after backslash, or bracketed - (let ((point (point)) - (open-start (cadr syntax)) - (backslash (python-backslash-continuation-line-p)) - (colon (eq ?: (char-before (1- (line-beginning-position)))))) - (if open-start - ;; Inside bracketed expression. - (progn - (goto-char (1+ open-start)) - ;; Look for first item in list (preceding point) and - ;; align with it, if found. - (if (with-syntax-table python-space-backslash-table - (let ((parse-sexp-ignore-comments t)) - (condition-case () - (progn (forward-sexp) - (backward-sexp) - (< (point) point)) - (error nil)))) - ;; Extra level if we're backslash-continued or - ;; following a key. - (if (or backslash colon) - (+ python-indent (current-column)) - (current-column)) - ;; Otherwise indent relative to statement start, one - ;; level per bracketing level. - (goto-char (1+ open-start)) - (python-beginning-of-statement) - (+ (current-indentation) (* (car syntax) python-indent)))) - ;; Otherwise backslash-continued. - (forward-line -1) - (if (python-continuation-line-p) - ;; We're past first continuation line. Align with - ;; previous line. - (current-indentation) - ;; First continuation line. Indent one step, with an - ;; extra one if statement opens a block. - (python-beginning-of-statement) - (+ (current-indentation) python-continuation-offset - (if (python-open-block-statement-p t) - python-indent - 0)))))) - ((bobp) 0) - ;; Fixme: Like python-mode.el; not convinced by this. - ((looking-at (rx (0+ space) (syntax comment-start) - (not (any " \t\n")))) ; non-indentable comment - (current-indentation)) - ((and python-honour-comment-indentation - ;; Back over whitespace, newlines, non-indentable comments. - (catch 'done - (while (cond ((bobp) nil) - ((not (forward-comment -1)) - nil) ; not at comment start - ;; Now at start of comment -- trailing one? - ((/= (current-column) (current-indentation)) - nil) - ;; Indentable comment, like python-mode.el? - ((and (looking-at (rx (syntax comment-start) - (or space line-end))) - (/= 0 (current-column))) - (throw 'done (current-column))) - ;; Else skip it (loop). - (t)))))) - (t - (python-indentation-levels) - ;; Prefer to indent comments with an immediately-following - ;; statement, e.g. - ;; ... - ;; # ... - ;; def ... - (when (and (> python-indent-list-length 1) - (python-comment-line-p)) - (forward-line) - (unless (python-comment-line-p) - (let ((elt (assq (current-indentation) python-indent-list))) - (setq python-indent-list - (nconc (delete elt python-indent-list) - (list elt)))))) - (caar (last python-indent-list))))))) - -;;;; Cycling through the possible indentations with successive TABs. - -;; These don't need to be buffer-local since they're only relevant -;; during a cycle. - -(defun python-initial-text () - "Text of line following indentation and ignoring any trailing comment." - (save-excursion - (buffer-substring (progn - (back-to-indentation) - (point)) - (progn - (end-of-line) - (forward-comment -1) - (point))))) - -(defconst python-block-pairs - '(("else" "if" "elif" "while" "for" "try" "except") - ("elif" "if" "elif") - ("except" "try" "except") - ("finally" "else" "try" "except")) - "Alist of keyword matches. -The car of an element is a keyword introducing a statement which -can close a block opened by a keyword in the cdr.") - -(defun python-first-word () - "Return first word (actually symbol) on the line." - (save-excursion - (back-to-indentation) - (current-word t))) - -(defun python-indentation-levels () - "Return a list of possible indentations for this line. -It is assumed not to be a continuation line or in a multi-line string. -Includes the default indentation and those which would close all -enclosing blocks. Elements of the list are actually pairs: -\(INDENTATION . TEXT), where TEXT is the initial text of the -corresponding block opening (or nil)." - (save-excursion - (let ((initial "") - levels indent) - ;; Only one possibility immediately following a block open - ;; statement, assuming it doesn't have a `suite' on the same line. - (cond - ((save-excursion (and (python-previous-statement) - (python-open-block-statement-p t) - (setq indent (current-indentation)) - ;; Check we don't have something like: - ;; if ...: ... - (if (progn (python-end-of-statement) - (python-skip-comments/blanks t) - (eq ?: (char-before))) - (setq indent (+ python-indent indent))))) - (push (cons indent initial) levels)) - ;; Only one possibility for comment line immediately following - ;; another. - ((save-excursion - (when (python-comment-line-p) - (forward-line -1) - (if (python-comment-line-p) - (push (cons (current-indentation) initial) levels))))) - ;; Fixme: Maybe have a case here which indents (only) first - ;; line after a lambda. - (t - (let ((start (car (assoc (python-first-word) python-block-pairs)))) - (python-previous-statement) - ;; Is this a valid indentation for the line of interest? - (unless (or (if start ; potentially only outdentable - ;; Check for things like: - ;; if ...: ... - ;; else ...: - ;; where the second line need not be outdented. - (not (member (python-first-word) - (cdr (assoc start - python-block-pairs))))) - ;; Not sensible to indent to the same level as - ;; previous `return' &c. - (python-close-block-statement-p)) - (push (cons (current-indentation) (python-initial-text)) - levels)) - (while (python-beginning-of-block) - (when (or (not start) - (member (python-first-word) - (cdr (assoc start python-block-pairs)))) - (push (cons (current-indentation) (python-initial-text)) - levels)))))) - (prog1 (or levels (setq levels '((0 . "")))) - (setq python-indent-list levels - python-indent-list-length (length python-indent-list)))))) - -;; This is basically what `python-indent-line' would be if we didn't -;; do the cycling. -(defun python-indent-line-1 (&optional leave) - "Subroutine of `python-indent-line'. -Does non-repeated indentation. LEAVE non-nil means leave -indentation if it is valid, i.e. one of the positions returned by -`python-calculate-indentation'." - (let ((target (python-calculate-indentation)) - (pos (- (point-max) (point)))) - (if (or (= target (current-indentation)) - ;; Maybe keep a valid indentation. - (and leave python-indent-list - (assq (current-indentation) python-indent-list))) - (if (< (current-column) (current-indentation)) - (back-to-indentation)) - (beginning-of-line) - (delete-horizontal-space) - (indent-to target) - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))))) - -(defun python-indent-line () - "Indent current line as Python code. -When invoked via `indent-for-tab-command', cycle through possible -indentations for current line. The cycle is broken by a command -different from `indent-for-tab-command', i.e. successive TABs do -the cycling." - (interactive) - (if (and (eq this-command 'indent-for-tab-command) - (eq last-command this-command)) - (if (= 1 python-indent-list-length) - (message "Sole indentation") - (progn (setq python-indent-index - (% (1+ python-indent-index) python-indent-list-length)) - (beginning-of-line) - (delete-horizontal-space) - (indent-to (car (nth python-indent-index python-indent-list))) - (if (python-block-end-p) - (let ((text (cdr (nth python-indent-index - python-indent-list)))) - (if text - (message "Closes: %s" text)))))) - (python-indent-line-1) - (setq python-indent-index (1- python-indent-list-length)))) - -(defun python-indent-region (start end) - "`indent-region-function' for Python. -Leaves validly-indented lines alone, i.e. doesn't indent to -another valid position." - (save-excursion - (goto-char end) - (setq end (point-marker)) - (goto-char start) - (or (bolp) (forward-line 1)) - (while (< (point) end) - (or (and (bolp) (eolp)) - (python-indent-line-1 t)) - (forward-line 1)) - (move-marker end nil))) - -(defun python-block-end-p () - "Non-nil if this is a line in a statement closing a block, -or a blank line indented to where it would close a block." - (and (not (python-comment-line-p)) - (or (python-close-block-statement-p t) - (< (current-indentation) - (save-excursion - (python-previous-statement) - (current-indentation)))))) - -;;;; Movement. - -;; Fixme: Define {for,back}ward-sexp-function? Maybe skip units like -;; block, statement, depending on context. - -(defun python-beginning-of-defun () - "`beginning-of-defun-function' for Python. -Finds beginning of innermost nested class or method definition. -Returns the name of the definition found at the end, or nil if -reached start of buffer." - (let ((ci (current-indentation)) - (def-re (rx line-start (0+ space) (or "def" "class") (1+ space) - (group (1+ (or word (syntax symbol)))))) - found lep) ;; def-line - (if (python-comment-line-p) - (setq ci most-positive-fixnum)) - (while (and (not (bobp)) (not found)) - ;; Treat bol at beginning of function as outside function so - ;; that successive C-M-a makes progress backwards. - ;;(setq def-line (looking-at def-re)) - (unless (bolp) (end-of-line)) - (setq lep (line-end-position)) - (if (and (re-search-backward def-re nil 'move) - ;; Must be less indented or matching top level, or - ;; equally indented if we started on a definition line. - (let ((in (current-indentation))) - (or (and (zerop ci) (zerop in)) - (= lep (line-end-position)) ; on initial line - ;; Not sure why it was like this -- fails in case of - ;; last internal function followed by first - ;; non-def statement of the main body. -;; (and def-line (= in ci)) - (= in ci) - (< in ci))) - (not (python-in-string/comment))) - (setq found t))) - found)) - -(defun python-end-of-defun () - "`end-of-defun-function' for Python. -Finds end of innermost nested class or method definition." - (let ((orig (point)) - (pattern (rx line-start (0+ space) (or "def" "class") space))) - ;; Go to start of current block and check whether it's at top - ;; level. If it is, and not a block start, look forward for - ;; definition statement. - (when (python-comment-line-p) - (end-of-line) - (forward-comment most-positive-fixnum)) - (if (not (python-open-block-statement-p)) - (python-beginning-of-block)) - (if (zerop (current-indentation)) - (unless (python-open-block-statement-p) - (while (and (re-search-forward pattern nil 'move) - (python-in-string/comment))) ; just loop - (unless (eobp) - (beginning-of-line))) - ;; Don't move before top-level statement that would end defun. - (end-of-line) - (python-beginning-of-defun)) - ;; If we got to the start of buffer, look forward for - ;; definition statement. - (if (and (bobp) (not (looking-at "def\\|class"))) - (while (and (not (eobp)) - (re-search-forward pattern nil 'move) - (python-in-string/comment)))) ; just loop - ;; We're at a definition statement (or end-of-buffer). - (unless (eobp) - (python-end-of-block) - ;; Count trailing space in defun (but not trailing comments). - (skip-syntax-forward " >") - (unless (eobp) ; e.g. missing final newline - (beginning-of-line))) - ;; Catch pathological cases like this, where the beginning-of-defun - ;; skips to a definition we're not in: - ;; if ...: - ;; ... - ;; else: - ;; ... # point here - ;; ... - ;; def ... - (if (< (point) orig) - (goto-char (point-max))))) - -(defun python-beginning-of-statement () - "Go to start of current statement. -Accounts for continuation lines, multi-line strings, and -multi-line bracketed expressions." - (while - (if (python-backslash-continuation-line-p) - (progn (forward-line -1) t) - (beginning-of-line) - (or (python-beginning-of-string) - (python-skip-out)))) - (back-to-indentation)) - -(defun python-skip-out (&optional forward syntax) - "Skip out of any nested brackets. -Skip forward if FORWARD is non-nil, else backward. -If SYNTAX is non-nil it is the state returned by `syntax-ppss' at point. -Return non-nil if and only if skipping was done." - ;; FIXME: Use syntax-ppss-toplevel-pos. - (let ((depth (syntax-ppss-depth (or syntax (syntax-ppss)))) - (forward (if forward -1 1))) - (unless (zerop depth) - (if (> depth 0) - ;; Skip forward out of nested brackets. - (condition-case () ; beware invalid syntax - (progn (backward-up-list (* forward depth)) t) - (error nil)) - ;; Invalid syntax (too many closed brackets). - ;; Skip out of as many as possible. - (let (done) - (while (condition-case () - (progn (backward-up-list forward) - (setq done t)) - (error nil))) - done))))) - -(defun python-end-of-statement () - "Go to the end of the current statement and return point. -Usually this is the start of the next line, but if this is a -multi-line statement we need to skip over the continuation lines. -On a comment line, go to end of line." - (end-of-line) - (while (let (comment) - ;; Move past any enclosing strings and sexps, or stop if - ;; we're in a comment. - (while (let ((s (syntax-ppss))) - (cond ((eq 'comment (syntax-ppss-context s)) - (setq comment t) - nil) - ((eq 'string (syntax-ppss-context s)) - ;; Go to start of string and skip it. - (let ((pos (point))) - (goto-char (nth 8 s)) - (condition-case () ; beware invalid syntax - (progn (forward-sexp) t) - ;; If there's a mismatched string, make sure - ;; we still overall move *forward*. - (error (goto-char pos) (end-of-line))))) - ((python-skip-out t s)))) - (end-of-line)) - (and (not comment) - (not (eobp)) - (eq ?\\ (char-before)))) ; Line continued? - (end-of-line 2)) ; Try next line. - (point)) - -(defun python-previous-statement (&optional count) - "Go to start of previous statement. -With argument COUNT, do it COUNT times. Stop at beginning of buffer. -Return count of statements left to move." - (interactive "p") - (unless count (setq count 1)) - (if (< count 0) - (python-next-statement (- count)) - (python-beginning-of-statement) - (while (and (> count 0) (not (bobp))) - (python-skip-comments/blanks t) - (python-beginning-of-statement) - (unless (bobp) (setq count (1- count)))) - count)) - -(defun python-next-statement (&optional count) - "Go to start of next statement. -With argument COUNT, do it COUNT times. Stop at end of buffer. -Return count of statements left to move." - (interactive "p") - (unless count (setq count 1)) - (if (< count 0) - (python-previous-statement (- count)) - (beginning-of-line) - (let (bogus) - (while (and (> count 0) (not (eobp)) (not bogus)) - (python-end-of-statement) - (python-skip-comments/blanks) - (if (eq 'string (syntax-ppss-context (syntax-ppss))) - (setq bogus t) - (unless (eobp) - (setq count (1- count)))))) - count)) - -(defun python-beginning-of-block (&optional arg) - "Go to start of current block. -With numeric arg, do it that many times. If ARG is negative, call -`python-end-of-block' instead. -If point is on the first line of a block, use its outer block. -If current statement is in column zero, don't move and return nil. -Otherwise return non-nil." - (interactive "p") - (unless arg (setq arg 1)) - (cond - ((zerop arg)) - ((< arg 0) (python-end-of-block (- arg))) - (t - (let ((point (point))) - (if (or (python-comment-line-p) - (python-blank-line-p)) - (python-skip-comments/blanks t)) - (python-beginning-of-statement) - (let ((ci (current-indentation))) - (if (zerop ci) - (not (goto-char point)) ; return nil - ;; Look upwards for less indented statement. - (if (catch 'done -;;; This is slower than the below. -;;; (while (zerop (python-previous-statement)) -;;; (when (and (< (current-indentation) ci) -;;; (python-open-block-statement-p t)) -;;; (beginning-of-line) -;;; (throw 'done t))) - (while (and (zerop (forward-line -1))) - (when (and (< (current-indentation) ci) - (not (python-comment-line-p)) - ;; Move to beginning to save effort in case - ;; this is in string. - (progn (python-beginning-of-statement) t) - (python-open-block-statement-p t)) - (beginning-of-line) - (throw 'done t))) - (not (goto-char point))) ; Failed -- return nil - (python-beginning-of-block (1- arg))))))))) - -(defun python-end-of-block (&optional arg) - "Go to end of current block. -With numeric arg, do it that many times. If ARG is negative, -call `python-beginning-of-block' instead. -If current statement is in column zero and doesn't open a block, -don't move and return nil. Otherwise return t." - (interactive "p") - (unless arg (setq arg 1)) - (if (< arg 0) - (python-beginning-of-block (- arg)) - (while (and (> arg 0) - (let* ((point (point)) - (_ (if (python-comment-line-p) - (python-skip-comments/blanks t))) - (ci (current-indentation)) - (open (python-open-block-statement-p))) - (if (and (zerop ci) (not open)) - (not (goto-char point)) - (catch 'done - (while (zerop (python-next-statement)) - (when (or (and open (<= (current-indentation) ci)) - (< (current-indentation) ci)) - (python-skip-comments/blanks t) - (beginning-of-line 2) - (throw 'done t))))))) - (setq arg (1- arg))) - (zerop arg))) - -(defvar python-which-func-length-limit 40 - "Non-strict length limit for `python-which-func' output.") - -(defun python-which-func () - (let ((function-name (python-current-defun python-which-func-length-limit))) - (set-text-properties 0 (length function-name) nil function-name) - function-name)) - - -;;;; Imenu. - -;; For possibly speeding this up, here's the top of the ELP profile -;; for rescanning pydoc.py (2.2k lines, 90kb): -;; Function Name Call Count Elapsed Time Average Time -;; ==================================== ========== ============= ============ -;; python-imenu-create-index 156 2.430906 0.0155827307 -;; python-end-of-defun 155 1.2718260000 0.0082053290 -;; python-end-of-block 155 1.1898689999 0.0076765741 -;; python-next-statement 2970 1.024717 0.0003450225 -;; python-end-of-statement 2970 0.4332190000 0.0001458649 -;; python-beginning-of-defun 265 0.0918479999 0.0003465962 -;; python-skip-comments/blanks 3125 0.0753319999 2.410...e-05 - -(defvar python-recursing) -(defun python-imenu-create-index () - "`imenu-create-index-function' for Python. - -Makes nested Imenu menus from nested `class' and `def' statements. -The nested menus are headed by an item referencing the outer -definition; it has a space prepended to the name so that it sorts -first with `imenu--sort-by-name' (though, unfortunately, sub-menus -precede it)." - (unless (boundp 'python-recursing) ; dynamically bound below - ;; Normal call from Imenu. - (goto-char (point-min)) - ;; Without this, we can get an infloop if the buffer isn't all - ;; fontified. I guess this is really a bug in syntax.el. OTOH, - ;; _with_ this, imenu doesn't immediately work; I can't figure out - ;; what's going on, but it must be something to do with timers in - ;; font-lock. - ;; This can't be right, especially not when jit-lock is not used. --Stef - ;; (unless (get-text-property (1- (point-max)) 'fontified) - ;; (font-lock-fontify-region (point-min) (point-max))) - ) - (let (index-alist) ; accumulated value to return - (while (re-search-forward - (rx line-start (0+ space) ; leading space - (or (group "def") (group "class")) ; type - (1+ space) (group (1+ (or word ?_)))) ; name - nil t) - (unless (python-in-string/comment) - (let ((pos (match-beginning 0)) - (name (match-string-no-properties 3))) - (if (match-beginning 2) ; def or class? - (setq name (concat "class " name))) - (save-restriction - (narrow-to-defun) - (let* ((python-recursing t) - (sublist (python-imenu-create-index))) - (if sublist - (progn (push (cons (concat " " name) pos) sublist) - (push (cons name sublist) index-alist)) - (push (cons name pos) index-alist))))))) - (unless (boundp 'python-recursing) - ;; Look for module variables. - (let (vars) - (goto-char (point-min)) - (while (re-search-forward - (rx line-start (group (1+ (or word ?_))) (0+ space) "=") - nil t) - (unless (python-in-string/comment) - (push (cons (match-string 1) (match-beginning 1)) - vars))) - (setq index-alist (nreverse index-alist)) - (if vars - (push (cons "Module variables" - (nreverse vars)) - index-alist)))) - index-alist)) - -;;;; `Electric' commands. - -(defun python-electric-colon (arg) - "Insert a colon and maybe outdent the line if it is a statement like `else'. -With numeric ARG, just insert that many colons. With \\[universal-argument], -just insert a single colon." - (interactive "*P") - (self-insert-command (if (not (integerp arg)) 1 arg)) - (and (not arg) - (eolp) - (python-outdent-p) - (not (python-in-string/comment)) - (> (current-indentation) (python-calculate-indentation)) - (python-indent-line))) ; OK, do it -(put 'python-electric-colon 'delete-selection t) - -(defun python-backspace (arg) - "Maybe delete a level of indentation on the current line. -Do so if point is at the end of the line's indentation outside -strings and comments. -Otherwise just call `backward-delete-char-untabify'. -Repeat ARG times." - (interactive "*p") - (if (or (/= (current-indentation) (current-column)) - (bolp) - (python-continuation-line-p) - (python-in-string/comment)) - (backward-delete-char-untabify arg) - ;; Look for the largest valid indentation which is smaller than - ;; the current indentation. - (let ((indent 0) - (ci (current-indentation)) - (indents (python-indentation-levels)) - initial) - (dolist (x indents) - (if (< (car x) ci) - (setq indent (max indent (car x))))) - (setq initial (cdr (assq indent indents))) - (if (> (length initial) 0) - (message "Closes %s" initial)) - (delete-horizontal-space) - (indent-to indent)))) -(put 'python-backspace 'delete-selection 'supersede) - -;;;; pychecker - -(defcustom python-check-command "pychecker --stdlib" - "Command used to check a Python file." - :type 'string - :group 'python) - -(defvar python-saved-check-command nil - "Internal use.") - -;; After `sgml-validate-command'. -(defun python-check (command) - "Check a Python file (default current buffer's file). -Runs COMMAND, a shell command, as if by `compile'. -See `python-check-command' for the default." - (interactive - (list (read-string "Checker command: " - (or python-saved-check-command - (concat python-check-command " " - (let ((name (buffer-file-name))) - (if name - (file-name-nondirectory name)))))))) - (set (make-local-variable 'python-saved-check-command) command) - (require 'compile) ;To define compilation-* variables. - (save-some-buffers (not compilation-ask-about-save) nil) - (let ((compilation-error-regexp-alist - (cons '("(\\([^,]+\\), line \\([0-9]+\\))" 1 2) - compilation-error-regexp-alist))) - (compilation-start command))) - -;;;; Inferior mode stuff (following cmuscheme). - -(defcustom python-python-command "python" - "Shell command to run Python interpreter. -Any arguments can't contain whitespace." - :group 'python - :type 'string) - -(defcustom python-jython-command "jython" - "Shell command to run Jython interpreter. -Any arguments can't contain whitespace." - :group 'python - :type 'string) - -(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. - -Use \\[python-set-proc] to set the default value from a buffer with a -local value.") -(make-variable-buffer-local 'python-buffer) - -(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 `' &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-map - (let ((map (make-sparse-keymap))) - ;; This will inherit from comint-mode-map. - (define-key map "\C-c\C-l" 'python-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 if we have a normal prompt, - ;; i.e. '>>> ' and not '... '. See the comment before - ;; python-send-region. Fixme: uncomment these if we address that. - - ;; (define-key map [(meta ?\t)] 'python-complete-symbol) - ;; (define-key map "\C-c\C-f" 'python-describe-symbol) - map)) - -(defvar inferior-python-mode-syntax-table - (let ((st (make-syntax-table python-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)) - -(defvar python--prompt-regexp nil) - -(defun python--set-prompt-regexp () - (let ((prompt (cdr-safe (or (assoc python-python-command - python-shell-prompt-alist) - (assq t python-shell-prompt-alist)))) - (cprompt (cdr-safe (or (assoc python-python-command - python-shell-continuation-prompt-alist) - (assq t python-shell-continuation-prompt-alist))))) - (set (make-local-variable 'comint-prompt-regexp) - (concat "\\(" - (mapconcat 'identity - (delq nil (list prompt cprompt "^([Pp]db) ")) - "\\|") - "\\)")) - (set (make-local-variable 'python--prompt-regexp) prompt))) - -;; 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 \\[run-python]. - -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 - (setq mode-line-process '(":%s")) - (set (make-local-variable 'comint-input-filter) 'python-input-filter) - (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter - nil t) - (python--set-prompt-regexp) - (set (make-local-variable 'compilation-error-regexp-alist) - python-compilation-regexp-alist) - (compilation-shell-minor-mode 1)) - -(defcustom inferior-python-filter-regexp "\\`\\s-*\\S-?\\S-?\\s-*\\'" - "Input matching this regexp is not saved on the history list. -Default ignores all inputs of 0, 1, or 2 non-blank characters." - :type 'regexp - :group 'python) - -(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 - :version "23.3") - -(defun python-input-filter (str) - "`comint-input-filter' function for inferior Python. -Don't save anything for STR matching `inferior-python-filter-regexp'." - (not (string-match inferior-python-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))) - -;;;###autoload -(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)) - (python-check-version cmd) - (setq python-command cmd) - ;; Fixme: Consider making `python-buffer' buffer-local as a buffer - ;; (not a name) in Python buffers from which `run-python' &c is - ;; invoked. Would support multiple processes better. - (when (or new (not (comint-check-proc python-buffer))) - (with-current-buffer - (let* ((cmdlist - (append (python-args-to-list cmd) '("-i") - (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. - (python-send-string "import emacs") - ;; The following line was meant to ensure that we're at a prompt - ;; before doing anything else. However, this can cause Emacs to - ;; hang waiting for a response, if that Python function fails - ;; (i.e. raises an exception). - ;; (python-send-receive "print '_emacs_out ()'") - )) - (if (derived-mode-p 'python-mode) - (setq python-buffer (default-value 'python-buffer))) ; buffer-local - ;; Without this, help output goes into the inferior python buffer if - ;; 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 (python-proc)) - (goto-char (point-max)) - (compilation-forget-errors) - (python-send-string command) - (setq compilation-last-buffer (current-buffer))))) - -(defun python-send-region (start end) - "Send the region to the inferior Python process." - ;; 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" nil ".py")) - (command - ;; IPython puts the FakeModule module into __main__ so - ;; emacs.eexecfile becomes useless. - (if (string-match "^ipython" python-command) - (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 (python-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 (python-proc) string) - (unless (string-match "\n\\'" string) - ;; Make sure the text is properly LF-terminated. - (comint-send-string (python-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 (python-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 (python-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)) - -(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 `python-load-file', which uses the -value to determine defaults." - :type '(repeat function) - :group 'python) - -(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 (python-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 python-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'"))) - -;;;; Context-sensitive help. - -(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 view-return-to-alist) -(eval-when-compile (autoload 'help-buffer "help-fns")) - -(defvar python-imports) ; forward declaration - -;; 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 () - (setq buffer-read-only t) - (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*" (python-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." - (python-send-string string) - (let ((proc (python-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 (python-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 - (python-send-receive (format "emacs.eargs(%S, %s)" - symbol python-imports))) - ;; Try moving to symbol before enclosing parens. - (let ((s (syntax-ppss))) - (unless (zerop (car s)) - (when (eq ?\( (char-after (nth 1 s))) - (save-excursion - (goto-char (nth 1 s)) - (skip-syntax-backward "-") - (let ((point (point))) - (skip-chars-backward "a-zA-Z._") - (if (< (point) point) - (python-send-receive - (format "emacs.eargs(%S, %s)" - (buffer-substring-no-properties (point) point) - python-imports)))))))))))) - -;;;; Info-look functionality. - -(declare-function info-lookup-maybe-add-help "info-look" (&rest arg)) - -;;;###autoload -(defun python-after-info-look () - "Set up info-look for Python. -Used with `eval-after-load'." - (let* ((version (let ((s (shell-command-to-string (concat python-command - " -V")))) - (string-match "^Python \\([0-9]+\\.[0-9]+\\>\\)" s) - (match-string 1 s))) - ;; Whether info files have a Python version suffix, e.g. in Debian. - (versioned - (with-temp-buffer - (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. - -(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) - -;; 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 line-start (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. - ((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 `python-indent'. 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 python-indent)) - (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 `python-indent'. 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 python-indent)) - (indent-rigidly start end count)) - -(defun python-outline-level () - "`outline-level' function for Python mode. -The level is the number of `python-indent' steps of indentation -of current line." - (1+ (/ (current-indentation) python-indent))) - -;; 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)))))) - -;; Fixme: This fails the first time if the sub-process isn't already -;; running. Presumably a timing issue with i/o to the process. -(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<)))) - -(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))))) - -;;;; Skeletons - -(defcustom python-use-skeletons nil - "Non-nil means template skeletons will be automagically inserted. -This happens when pressing \"if\", for example, to prompt for -the if condition." - :type 'boolean - :group 'python) - -(define-abbrev-table 'python-mode-abbrev-table () - "Abbrev table for Python mode." - :case-fixed t - ;; Allow / inside abbrevs. - :regexp "\\(?:^\\|[^/]\\)\\<\\([[:word:]/]+\\)\\W*" - ;; Only expand in code. - :enable-function (lambda () (not (python-in-string/comment)))) - -(eval-when-compile - ;; Define a user-level skeleton and add it to the abbrev table. -(defmacro def-python-skeleton (name &rest elements) - (declare (indent 2)) - (let* ((name (symbol-name name)) - (function (intern (concat "python-insert-" name)))) - `(progn - ;; Usual technique for inserting a skeleton, but expand - ;; to the original abbrev instead if in a comment or string. - (when python-use-skeletons - (define-abbrev python-mode-abbrev-table ,name "" - ',function - nil t)) ; system abbrev - (define-skeleton ,function - ,(format "Insert Python \"%s\" template." name) - ,@elements))))) - -;; From `skeleton-further-elements' set below: -;; `<': outdent a level; -;; `^': delete indentation on current line and also previous newline. -;; Not quite like `delete-indentation'. Assumes point is at -;; beginning of indentation. - -(def-python-skeleton if - "Condition: " - "if " str ":" \n - > -1 ; Fixme: I don't understand the spurious space this removes. - _ \n - ("other condition, %s: " - < ; Avoid wrong indentation after block opening. - "elif " str ":" \n - > _ \n nil) - '(python-else) | ^) - -(define-skeleton python-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) - -(def-python-skeleton while - "Condition: " - "while " str ":" \n - > -1 _ \n - '(python-else) | ^) - -(def-python-skeleton for - "Target, %s: " - "for " str " in " (skeleton-read "Expression, %s: ") ":" \n - > -1 _ \n - '(python-else) | ^) - -(def-python-skeleton try/except - nil - "try:" \n - > -1 _ \n - ("Exception, %s: " - < "except " str '(python-target) ":" \n - > _ \n nil) - < "except:" \n - > _ \n - '(python-else) | ^) - -(define-skeleton python-target - "Auxiliary skeleton." - "Target, %s: " ", " str | -2) - -(def-python-skeleton try/finally - nil - "try:" \n - > -1 _ \n - < "finally:" \n - > _ \n) - -(def-python-skeleton def - "Name: " - "def " str " (" ("Parameter, %s: " (unless (equal ?\( (char-before)) ", ") - str) "):" \n - "\"\"\"" - "\"\"\"" \n ; Fixme: extra space inserted -- why?). - > _ \n) - -(def-python-skeleton class - "Name: " - "class " str " (" ("Inheritance, %s: " - (unless (equal ?\( (char-before)) ", ") - str) - & ")" | -2 ; close list or remove opening - ":" \n - "\"\"\"" - "\"\"\"" \n - > _ \n) - -(defvar python-default-template "if" - "Default template to expand by `python-expand-template'. -Updated on each expansion.") - -(defun python-expand-template (name) - "Expand template named NAME. -Interactively, prompt for the name with completion." - (interactive - (list (completing-read (format "Template to expand (default %s): " - python-default-template) - python-mode-abbrev-table nil t nil nil - python-default-template))) - (if (equal "" name) - (setq name python-default-template) - (setq python-default-template name)) - (let ((sym (abbrev-symbol name python-mode-abbrev-table))) - (if sym - (abbrev-insert sym) - (error "Undefined template: %s" name)))) - -;;;; Bicycle Repair Man support - -(autoload 'pymacs-load "pymacs" nil t) -(autoload 'brm-init "bikeemacs") -(defvar brm-menu) - -;; I'm not sure how useful BRM really is, and it's certainly dangerous -;; the way it modifies files outside Emacs... Also note that the -;; current BRM loses with tabs used for indentation -- I submitted a -;; fix . -(defun python-setup-brm () - "Set up Bicycle Repair Man refactoring tool (if available). - -Note that the `refactoring' features change files independently of -Emacs and may modify and save the contents of the current buffer -without confirmation." - (interactive) - (condition-case data - (unless (fboundp 'brm-rename) - (pymacs-load "bikeemacs" "brm-") ; first line of normal recipe - (let ((py-mode-map (make-sparse-keymap)) ; it assumes this - (features (cons 'python-mode features))) ; and requires this - (brm-init) ; second line of normal recipe - (remove-hook 'python-mode-hook ; undo this from `brm-init' - (lambda () (easy-menu-add brm-menu))) - (easy-menu-define - python-brm-menu python-mode-map - "Bicycle Repair Man" - '("BicycleRepairMan" - :help "Interface to navigation and refactoring tool" - "Queries" - ["Find References" brm-find-references - :help "Find references to name at point in compilation buffer"] - ["Find Definition" brm-find-definition - :help "Find definition of name at point"] - "-" - "Refactoring" - ["Rename" brm-rename - :help "Replace name at point with a new name everywhere"] - ["Extract Method" brm-extract-method - :active (and mark-active (not buffer-read-only)) - :help "Replace statements in region with a method"] - ["Extract Local Variable" brm-extract-local-variable - :active (and mark-active (not buffer-read-only)) - :help "Replace expression in region with an assignment"] - ["Inline Local Variable" brm-inline-local-variable - :help - "Substitute uses of variable at point with its definition"] - ;; Fixme: Should check for anything to revert. - ["Undo Last Refactoring" brm-undo :help ""])))) - (error (error "BicycleRepairMan setup failed: %s" data)))) - -;;;; Modes. - -;; 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) - -(defvar outline-heading-end-regexp) -(defvar eldoc-documentation-function) -(defvar python-mode-running) ;Dynamically scoped var. - -;;;###autoload -(define-derived-mode python-mode prog-mode "Python" - "Major mode for editing Python files. -Turns on Font Lock mode unconditionally since it is currently required -for correct parsing of the source. -See also `jython-mode', which is actually invoked if the buffer appears to -contain Jython code. See also `run-python' and associated Python mode -commands for running Python under Emacs. - -The Emacs commands which work with `defun's, e.g. \\[beginning-of-defun], deal -with nested `def' and `class' blocks. They take the innermost one as -current without distinguishing method and class definitions. Used multiple -times, they move over others at the same indentation level until they reach -the end of definitions at that level, when they move up a level. -\\ -Colon is electric: it outdents the line if appropriate, e.g. for -an else statement. \\[python-backspace] at the beginning of an indented statement -deletes a level of indentation to close the current block; otherwise it -deletes a character backward. TAB indents the current line relative to -the preceding code. Successive TABs, with no intervening command, cycle -through the possibilities for indentation on the basis of enclosing blocks. - -\\[fill-paragraph] fills comments and multi-line strings appropriately, but has no -effect outside them. - -Supports Eldoc mode (only for functions, using a Python process), -Info-Look and Imenu. In Outline minor mode, `class' and `def' -lines count as headers. Symbol completion is available in the -same way as in the Python shell using the `rlcompleter' module -and this is added to the Hippie Expand functions locally if -Hippie Expand mode is turned on. Completion of symbols of the -form x.y only works if the components are literal -module/attribute names, not variables. An abbrev table is set up -with skeleton expansions for compound statement templates. - -\\{python-mode-map}" - :group 'python - (set (make-local-variable 'font-lock-defaults) - '(python-font-lock-keywords nil nil nil nil - ;; This probably isn't worth it. - ;; (font-lock-syntactic-face-function - ;; . python-font-lock-syntactic-face-function) - )) - (set (make-local-variable 'syntax-propertize-function) - python-syntax-propertize-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 'indent-line-function) #'python-indent-line) - (set (make-local-variable 'indent-region-function) #'python-indent-region) - (set (make-local-variable 'paragraph-start) "\\s-*$") - (set (make-local-variable 'fill-paragraph-function) 'python-fill-paragraph) - (set (make-local-variable 'require-final-newline) mode-require-final-newline) - (set (make-local-variable 'add-log-current-defun-function) - #'python-current-defun) - (set (make-local-variable 'outline-regexp) - (rx (* space) (or "class" "def" "elif" "else" "except" "finally" - "for" "if" "try" "while" "with") - symbol-end)) - (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 'beginning-of-defun-function) - 'python-beginning-of-defun) - (set (make-local-variable 'end-of-defun-function) 'python-end-of-defun) - (add-hook 'which-func-functions 'python-which-func nil t) - (setq imenu-create-index-function #'python-imenu-create-index) - (set (make-local-variable 'eldoc-documentation-function) - #'python-eldoc-function) - (add-hook 'eldoc-mode-hook - (lambda () (run-python nil t)) ; need it running - nil t) - (add-hook 'completion-at-point-functions - 'python-completion-at-point nil 'local) - (set (make-local-variable 'skeleton-further-elements) - '((< '(backward-delete-char-untabify (min python-indent - (current-column)))) - (^ '(- (1+ (current-indentation)))))) - ;; Python defines TABs as being 8-char wide. - (set (make-local-variable 'tab-width) 8) - (when python-guess-indent (python-guess-indent)) - ;; Let's make it harder for the user to shoot himself in the foot. - (unless (= tab-width python-indent) - (setq indent-tabs-mode nil)) - (set (make-local-variable 'python-command) python-python-command) - (python-find-imports) - (unless (boundp 'python-mode-running) ; kill the recursion from jython-mode - (let ((python-mode-running t)) - (python-maybe-jython)))) - -;; Not done automatically in Emacs 21 or 22. -(defcustom python-mode-hook nil - "Hook run when entering Python mode." - :group 'python - :type 'hook) -(custom-add-option 'python-mode-hook 'imenu-add-menubar-index) -(custom-add-option 'python-mode-hook - (lambda () - "Turn off Indent Tabs mode." - (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) - -;;;###autoload -(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 - (set (make-local-variable 'python-command) python-jython-command)) - - - -;; pdbtrack features - -(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 - (line-beginning-position) - (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 file system (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 block (ansi-color-filter-apply block)) - (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)) - (msg (get-text-property 0 'compilation-message filename)) - (loc (and msg (compilation--message->loc msg))) - funcbuffer) - - (cond ((and loc (markerp (compilation--loc->marker loc))) - (setq funcbuffer (marker-buffer (compilation--loc->marker loc))) - (list (with-current-buffer funcbuffer - (line-number-at-pos (compilation--loc->marker loc))) - funcbuffer)) - - ((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))) - -(defun python-pdbtrack-toggle-stack-tracking (arg) - (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 python-pdbtrack-do-tracking-p (not python-pdbtrack-do-tracking-p)) - (setq python-pdbtrack-do-tracking-p (> arg 0))) - (message "%sabled Python's pdbtrack" - (if python-pdbtrack-do-tracking-p "En" "Dis"))) - -(defun turn-on-pdbtrack () - (interactive) - (python-pdbtrack-toggle-stack-tracking 1)) - -(defun turn-off-pdbtrack () - (interactive) - (python-pdbtrack-toggle-stack-tracking 0)) - -(defun python-sentinel (_proc _msg) - (setq overlay-arrow-position nil)) - -(defun python-unload-function () - "Unload the Python library." - (remove-hook 'comint-output-filter-functions 'python-pdbtrack-track-stack-file) - (setq minor-mode-alist (assq-delete-all 'python-pdbtrack-is-tracking-p - minor-mode-alist)) - (dolist (error '("^No symbol" "^Can't shift all lines enough")) - (setq debug-ignored-errors (delete error debug-ignored-errors))) - ;; continue standard unloading - nil) - -;;;; Finish up -;; Fixme: should be in hideshow. This seems to be of limited use -;; since it isn't (can't be) indentation-based. Also hide-level -;; doesn't seem to work properly. -(add-to-list 'hs-special-modes-alist - `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" - ,(lambda (_arg) - (python-end-of-defun) - (skip-chars-backward " \t\n")) - nil)) - -(provide 'python) -(provide 'python-21) - -;;; python.el ends here -- 2.11.4.GIT