From acaf905b1130aae80fa59d2c861ffd4c8eb75486 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Thu, 5 Jan 2012 01:46:05 -0800 Subject: Add 2012 to FSF copyright years for Emacs files --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6081d8e838b..0c29891cd92 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,6 +1,6 @@ ;;; python.el --- silly walks for Python -*- coding: iso-8859-1 -*- -;; Copyright (C) 2003-2011 Free Software Foundation, Inc. +;; Copyright (C) 2003-2012 Free Software Foundation, Inc. ;; Author: Dave Love ;; Maintainer: FSF -- cgit v1.2.1 From 6b25e4e27d3386d08d2b5d64ea717780c05f5c65 Mon Sep 17 00:00:00 2001 From: Syver Enstad Date: Sun, 29 Jan 2012 15:06:54 +0800 Subject: Fix pdb path handling. * progmodes/gud.el (pdb): Give pdb full paths, to allow setting breakpoints in files outside current directory. * lisp/progmodes/python.el: Require ansi-color at top-level. Fixes: debbugs:6098 --- lisp/progmodes/python.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0c29891cd92..15d98ce48af 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -67,6 +67,7 @@ ;;; Code: (require 'comint) +(require 'ansi-color) (eval-when-compile (require 'compile) @@ -1386,7 +1387,6 @@ For running multiple processes in multiple buffers, see `run-python' and \\{inferior-python-mode-map}" :group 'python - (require 'ansi-color) ; for ipython (setq mode-line-process '(":%s")) (set (make-local-variable 'comint-input-filter) 'python-input-filter) (add-hook 'comint-preoutput-filter-functions #'python-preoutput-filter @@ -1530,7 +1530,6 @@ 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))) - (require 'ansi-color) ; for ipython (unless cmd (setq cmd python-command)) (python-check-version cmd) (setq python-command cmd) -- cgit v1.2.1 From 5eac0c026f56f6159d871ea92d869eef56bf66ff Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Sat, 11 Feb 2012 12:20:16 +0800 Subject: Fix infinite loop in python-end-of-statement http://debbugs.gnu.org/10788 --- lisp/progmodes/python.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 15d98ce48af..3ef872d26eb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1004,9 +1004,10 @@ On a comment line, go to end of line." (error (goto-char pos) (end-of-line))))) ((python-skip-out t s)))) (end-of-line)) - (unless comment - (eq ?\\ (char-before)))) ; Line continued? - (end-of-line 2)) ; Try next 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) -- cgit v1.2.1 From fb7ada5f94a71d73bf6cfad5cc87fe7fb26125a5 Mon Sep 17 00:00:00 2001 From: Chong Yidong Date: Mon, 9 Apr 2012 21:05:48 +0800 Subject: Remove * characters from the front of variable docstrings. --- lisp/progmodes/python.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3ef872d26eb..f7566c31b41 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -494,7 +494,7 @@ statement." (defcustom python-pdbtrack-do-tracking-p t - "*Controls whether the pdbtrack feature is enabled or not. + "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. @@ -509,7 +509,7 @@ having to restart the program." (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." + "Minor-mode sign to be displayed when pdbtrack is active." :type 'string :group 'python) @@ -1288,7 +1288,7 @@ 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. + "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 -- cgit v1.2.1 From 8f33b5f8734810e2feb0036bd9b0b34f51f7bc17 Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Tue, 10 Apr 2012 13:27:44 +0200 Subject: `python-fill-paragraph' filling fixup when font-lock is disabled * progmodes/python.el (python-fill-paragraph): Make python-fill-region in a multiline string work when font-lock is disabled. Fixes: debbugs:7018 --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f7566c31b41..09b89993626 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1959,7 +1959,7 @@ the string's indentation." ;; paragraph in a multi-line string properly, so narrow ;; to the string and then fill around (the end of) the ;; current line. - ((eq t (nth 3 syntax)) ; in fenced string + ((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 -- cgit v1.2.1 From f45f90f33151dc74ef541cba4fe87565215382a0 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Tue, 17 Apr 2012 03:28:57 +0800 Subject: * lisp/progmodes/python.el: Trivial cleanup. --- lisp/progmodes/python.el | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 09b89993626..6f8758ebec1 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -69,11 +69,7 @@ (require 'comint) (require 'ansi-color) -(eval-when-compile - (require 'compile) - (require 'hippie-exp)) - -(autoload 'comint-mode "comint") +(eval-when-compile (require 'compile)) (defgroup python nil "Silly walks in the Python language." @@ -1488,8 +1484,6 @@ Don't save anything for STR matching `inferior-python-filter-regexp'." res) (t (concat res s))))) -(autoload 'comint-check-proc "comint") - (defvar python-version-checked nil) (defun python-check-version (cmd) "Check that CMD runs a suitable version of Python." @@ -1684,8 +1678,6 @@ value to determine defaults." "Caches (directory . file) pair used in the last `python-load-file' command. Used for determining the default in the next one.") -(autoload 'comint-get-source "comint") - (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. -- cgit v1.2.1 From 376cbaccd90fe54837eb452f5d6c1cda42e0e8fc Mon Sep 17 00:00:00 2001 From: Juanma Barranquero Date: Thu, 19 Apr 2012 18:50:07 +0200 Subject: Remove some `toggle-read-only' warnings. * lisp/bs.el (bs-toggle-readonly): Call `toggle-read-only' interactively. * lisp/descr-text.el (describe-char): lisp/progmodes/python.el (python-describe-symbol): Don't call `toggle-read-only', set `buffer-read-only'. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6f8758ebec1..2922330e6f9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1768,7 +1768,7 @@ will." ;; allow C-c C-f in help buffer. (let ((temp-buffer-show-hook ; avoid xref stuff (lambda () - (toggle-read-only 1) + (setq buffer-read-only t) (setq view-return-to-alist (list (cons (selected-window) help-return-method)))))) (with-output-to-temp-buffer (help-buffer) -- cgit v1.2.1 From 257440aa1c368048203a111db15f5a791a07f53f Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Tue, 24 Apr 2012 22:58:29 +0800 Subject: * lisp/progmodes/python.el: Move hideshow setup to the end. --- lisp/progmodes/python.el | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2922330e6f9..f88d77d214d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2468,15 +2468,6 @@ with skeleton expansions for compound statement templates. nil t) (add-hook 'completion-at-point-functions 'python-completion-at-point nil 'local) - ;; 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)) (set (make-local-variable 'skeleton-further-elements) '((< '(backward-delete-char-untabify (min python-indent (current-column)))) @@ -2717,6 +2708,17 @@ comint believe the user typed this string so that ;; 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) -- cgit v1.2.1 From daf75653c2f1301332eb6c8af830050794ae0877 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Wed, 25 Apr 2012 23:23:19 +0800 Subject: * progmodes/python.el (python-send-region): Add suffix .py --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f88d77d214d..b00e42db4b7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1601,7 +1601,7 @@ behavior, change `python-remove-cwd-from-path' to nil." ;; Fixme: Write a `coding' header to the temp file if the region is ;; non-ASCII. (interactive "r") - (let* ((f (make-temp-file "py")) + (let* ((f (make-temp-file "py" nil ".py")) (command ;; IPython puts the FakeModule module into __main__ so ;; emacs.eexecfile becomes useless. -- cgit v1.2.1 From 784e7d6eebb4ac47d1f7b4e724b7aa843bc02f81 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Thu, 26 Apr 2012 00:46:01 +0800 Subject: * lisp/progmodes/python.el (python-pdbtrack-get-source-buffer): Use compilation-message if available to find real filename. --- lisp/progmodes/python.el | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b00e42db4b7..620ea8cd519 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2606,9 +2606,17 @@ problem." (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 ((file-exists-p filename) + (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)) @@ -2626,15 +2634,12 @@ problem." (buffer-substring (point-min) (point-max))) ))))))) - (list lineno funcbuffer)) + (list lineno funcbuffer)) ((= (elt filename 0) ?\<) (format "(Non-file source: '%s')" filename)) - (t (format "Not found: %s(), %s" funcname 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." -- cgit v1.2.1 From d8c80081f67fd7674a0a7391b82fe0431ddb6552 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:01:30 -0300 Subject: 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 (limited to '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 -- cgit v1.2.1 From 3b3027dc0af9f75395d225a57c1c89300ed95b8f Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:42 -0300 Subject: Added blank python.el to workaround bzr git-apply issues --- lisp/progmodes/python.el | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lisp/progmodes/python.el (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 45c138ac0ff103d83e1fd5ba5893b70c4a6d316e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:52 -0300 Subject: First commit. --- lisp/progmodes/python.el | 1640 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1640 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e69de29bb2d..b281c01b7d9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -0,0 +1,1640 @@ +;;; python.el -- Python's flying circus support for Emacs + +;; Copyright (C) 2010 Free Software Foundation, Inc. + +;; Author: FabiĆ”n E. Gallina +;; Maintainer: FSF +;; Created: Jul 2010 +;; Keywords: languages + +;; This file is NOT part of GNU Emacs. + +;; python.el 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. + +;; python.el 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 python.el. If not, see . + +;;; Commentary: + +;; Major mode for editing Python files with some fontification and +;; indentation bits extracted from original Dave Love's python.el +;; found in GNU/Emacs. + +;; While it probably has less features than Dave Love's python.el and +;; PSF's python-mode.el it provides the main stuff you'll need while +;; keeping it simple :) + +;; Implements Syntax highlighting, Indentation, Movement, Shell +;; interaction, Shell completion, Pdb tracking, Symbol completion, +;; Eldoc. + +;; Syntax highlighting: Fontification of code is provided and supports +;; python's triple quoted strings properly. + +;; Indentation: Automatic indentation with indentation cycling is +;; provided, it allows you to navigate different available levels of +;; indentation by hitting several times. + +;; Movement: `beginning-of-defun' and `end-of-defun' functions are +;; properly implemented. A `beginning-of-innermost-defun' is defined +;; to navigate nested defuns. + +;; Shell interaction: is provided and allows you easily execute any +;; block of code of your current buffer in an inferior Python process. + +;; Shell completion: hitting tab will try to complete the current +;; word. Shell completion is implemented in a manner that if you +;; change the `python-shell-interpreter' to any other (for example +;; IPython) it should be easy to integrate another way to calculate +;; completions. You just need to especify your custom +;; `python-shell-completion-setup-code' and +;; `python-shell-completion-strings-code' + +;; Pdb tracking: when you execute a block of code that contains some +;; call to pdb (or ipdb) it will prompt the block of code and will +;; follow the execution of pdb marking the current line with an arrow. + +;; Symbol completion: you can complete the symbol at point. It uses +;; the shell completion in background so you should run +;; `python-shell-send-buffer' from time to time to get better results. + +;; Eldoc: returns documentation for object at point by using the +;; inferior python subprocess to inspect its documentation. As you +;; might guessed you should run `python-shell-send-buffer' from time +;; to time to get better results too. + +;;; Installation: + +;; Add this to your .emacs: + +;; (add-to-list 'load-path "/folder/containing/file") +;; (require 'python) + +;;; TODO: + +;; Ordered by priority: + +;; Better decorator support for beginning of defun + +;; Fix shell autocompletion when: obj. + +;; Remove garbage prompts left from calls to `comint-send-string' and +;; other comint related cleanups. + +;; Review code and cleanup + +;;; Code: + +(require 'comint) +(require 'ansi-color) +(require 'outline) + +(eval-when-compile + (require 'cl)) + +(autoload 'comint-mode "comint") + +;;;###autoload +(add-to-list 'auto-mode-alist (cons (purecopy "\\.py\\'") 'python-mode)) +;;;###autoload +(add-to-list 'interpreter-mode-alist (cons (purecopy "python") 'python-mode)) + +(defgroup python nil + "Python Language's flying circus support for Emacs." + :group 'languages + :version "23.2" + :link '(emacs-commentary-link "python")) + + +;;; Bindings + +(defvar python-mode-map + (let ((map (make-sparse-keymap))) + ;; Indent specific + (define-key map "\177" 'python-indent-dedent-line-backspace) + (define-key map (kbd "") 'python-indent-dedent-line) + (define-key map "\C-c<" 'python-indent-shift-left) + (define-key map "\C-c>" 'python-indent-shift-right) + ;; Shell interaction + (define-key map "\C-c\C-s" 'python-shell-send-string) + (define-key map "\C-c\C-r" 'python-shell-send-region) + (define-key map "\C-\M-x" 'python-shell-send-defun) + (define-key map "\C-c\C-c" 'python-shell-send-buffer) + (define-key map "\C-c\C-l" 'python-shell-send-file) + (define-key map "\C-c\C-z" 'python-shell-switch-to-shell) + ;; Utilities + (substitute-key-definition 'complete-symbol 'completion-at-point + map global-map) + (easy-menu-define python-menu map "Python Mode menu" + `("Python" + :help "Python-specific Features" + ["Shift region left" python-indent-shift-left :active mark-active + :help "Shift region left by a single indentation step"] + ["Shift region right" python-indent-shift-right :active mark-active + :help "Shift region right by a single indentation step"] + "-" + ["Mark def/class" mark-defun + :help "Mark outermost definition around point"] + "-" + ["Start of def/class" beginning-of-defun + :help "Go to start of outermost definition around point"] + ["Start of def/class" python-beginning-of-innermost-defun + :help "Go to start of innermost definition around point"] + ["End of def/class" end-of-defun + :help "Go to end of definition around point"] + "-" + ["Start interpreter" run-python + :help "Run inferior Python process in a separate buffer"] + ["Switch to shell" python-shell-switch-to-shell + :help "Switch to running inferior Python process"] + ["Eval string" python-shell-send-string + :help "Eval string in inferior Python session"] + ["Eval buffer" python-shell-send-buffer + :help "Eval buffer in inferior Python session"] + ["Eval region" python-shell-send-region + :help "Eval region in inferior Python session"] + ["Eval defun" python-shell-send-defun + :help "Eval defun in inferior Python session"] + ["Eval file" python-shell-send-file + :help "Eval file in inferior Python session"] + ["Debugger" pdb :help "Run pdb under GUD"] + "-" + ["Complete symbol" completion-at-point + :help "Complete symbol before point"])) + map) + "Keymap for `python-mode'.") + + +;;; Python specialized rx + +(defconst python-rx-constituents + (list + `(block-start . ,(rx symbol-start + (or "def" "class" "if" "elif" "else" "try" + "except" "finally" "for" "while" "with") + symbol-end)) + `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) + `(open-paren . ,(rx (or "{" "[" "("))) + `(close-paren . ,(rx (or "}" "]" ")"))) + `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) + `(not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) + `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" + "=" "%" "**" "//" "<<" ">>" "<=" "!=" + "==" ">=" "is" "not"))) + `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" + ">>=" "<<=" "&=" "^=" "|="))))) + +(defmacro python-rx (&rest regexps) + "Python mode especialized rx macro which supports common python named regexps." + (let ((rx-constituents (append python-rx-constituents rx-constituents))) + (cond ((null regexps) + (error "No regexp")) + ((cdr regexps) + (rx-to-string `(and ,@regexps) t)) + (t + (rx-to-string (car regexps) t))))) + + +;;; Font-lock and syntax + +(defvar python-font-lock-keywords + ;; Keywords + `(,(rx symbol-start + (or "and" "del" "from" "not" "while" "as" "elif" "global" "or" "with" + "assert" "else" "if" "pass" "yield" "break" "except" "import" + "print" "class" "exec" "in" "raise" "continue" "finally" "is" + "return" "def" "for" "lambda" "try" "self") + symbol-end) + ;; functions + (,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) + (1 font-lock-function-name-face)) + ;; classes + (,(rx symbol-start "class" (1+ space) (group (1+ (or word ?_)))) + (1 font-lock-type-face)) + ;; Constants + (,(rx symbol-start (group "None" symbol-end)) + (1 font-lock-constant-face)) + ;; Decorators. + (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) + (0+ "." (1+ (or word ?_))))) + (1 font-lock-type-face)) + ;; Builtin Exceptions + (,(rx symbol-start + (or "ArithmeticError" "AssertionError" "AttributeError" + "BaseException" "BufferError" "BytesWarning" "DeprecationWarning" + "EOFError" "EnvironmentError" "Exception" "FloatingPointError" + "FutureWarning" "GeneratorExit" "IOError" "ImportError" + "ImportWarning" "IndentationError" "IndexError" "KeyError" + "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" + "NotImplemented" "NotImplementedError" "OSError" "OverflowError" + "PendingDeprecationWarning" "ReferenceError" "RuntimeError" + "RuntimeWarning" "StandardError" "StopIteration" "SyntaxError" + "SyntaxWarning" "SystemError" "SystemExit" "TabError" "TypeError" + "UnboundLocalError" "UnicodeDecodeError" "UnicodeEncodeError" + "UnicodeError" "UnicodeTranslateError" "UnicodeWarning" + "UserWarning" "ValueError" "Warning" "ZeroDivisionError") + symbol-end) . font-lock-type-face) + ;; Builtins + (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start + (group + (or "_" "__debug__" "__doc__" "__import__" "__name__" "__package__" + "abs" "all" "any" "apply" "basestring" "bin" "bool" "buffer" + "bytearray" "bytes" "callable" "chr" "classmethod" "cmp" "coerce" + "compile" "complex" "copyright" "credits" "delattr" "dict" "dir" + "divmod" "enumerate" "eval" "execfile" "exit" "file" "filter" + "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" + "help" "hex" "id" "input" "int" "intern" "isinstance" "issubclass" + "iter" "len" "license" "list" "locals" "long" "map" "max" "min" + "next" "object" "oct" "open" "ord" "pow" "print" "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" + "True" "False" "Ellipsis")) symbol-end) + (1 font-lock-builtin-face)) + ;; asignations + ;; support for a = b = c = 5 + (,(lambda (limit) + (let ((re (python-rx (group (+ (any word ?. ?_))) (* space) + assignment-operator))) + (when (re-search-forward re limit t) + (while (and (not (equal (nth 0 (syntax-ppss)) 0)) + (re-search-forward re limit t))) + (if (equal (nth 0 (syntax-ppss)) 0) + t + (set-match-data nil))))) + (1 font-lock-variable-name-face nil nil)) + ;; support for a, b, c = (1, 2, 3) + (,(lambda (limit) + (let ((re (python-rx (group (+ (any word ?. ?_))) (* space) + (* ?, (* space) (+ (any word ?. ?_)) (* space)) + ?, (* space) (+ (any word ?. ?_)) (* space) + assignment-operator))) + (when (and (re-search-forward re limit t) + (goto-char (nth 3 (match-data)))) + (while (and (not (equal (nth 0 (syntax-ppss)) 0)) + (re-search-forward re limit t)) + (goto-char (nth 3 (match-data)))) + (if (equal (nth 0 (syntax-ppss)) 0) + t + (set-match-data nil))))) + (1 font-lock-variable-name-face nil nil)))) + +;; Fixme: Is there a better way? +(defconst python-font-lock-syntactic-keywords + ;; First avoid a sequence preceded by an odd number of backslashes. + `((,(rx (not (any ?\\)) + ?\\ (* (and ?\\ ?\\)) + (group (syntax string-quote)) + (backref 1) + (group (backref 1))) + (2 ,(string-to-syntax "\""))) ; dummy + (,(rx (group (optional (any "uUrR"))) ; prefix gets syntax property + (optional (any "rR")) ; possible second prefix + (group (syntax string-quote)) ; maybe gets property + (backref 2) ; per first quote + (group (backref 2))) ; maybe gets property + (1 (python-quote-syntax 1)) + (2 (python-quote-syntax 2)) + (3 (python-quote-syntax 3)))) + "Make outer chars of triple-quote strings into generic string delimiters.") + +(defun python-quote-syntax (n) + "Put `syntax-table' property correctly on triple quote. +Used for syntactic keywords. N is the match number (1, 2 or 3)." + ;; Given a triple quote, we have to check the context to know + ;; whether this is an opening or closing triple or whether it's + ;; quoted anyhow, and should be ignored. (For that we need to do + ;; the same job as `syntax-ppss' to be correct and it seems to be OK + ;; to use it here despite initial worries.) We also have to sort + ;; out a possible prefix -- well, we don't _have_ to, but I think it + ;; should be treated as part of the string. + + ;; Test cases: + ;; ur"""ar""" x='"' # """ + ;; x = ''' """ ' a + ;; ''' + ;; x '"""' x """ \"""" x + (save-excursion + (goto-char (match-beginning 0)) + (cond + ;; Consider property for the last char if in a fenced string. + ((= n 3) + (let* ((font-lock-syntactic-keywords nil) + (syntax (syntax-ppss))) + (when (eq t (nth 3 syntax)) ; after unclosed fence + (goto-char (nth 8 syntax)) ; fence position + (skip-chars-forward "uUrR") ; skip any prefix + ;; Is it a matching sequence? + (if (eq (char-after) (char-after (match-beginning 2))) + (eval-when-compile (string-to-syntax "|")))))) + ;; Consider property for initial char, accounting for prefixes. + ((or (and (= n 2) ; leading quote (not prefix) + (= (match-beginning 1) (match-end 1))) ; prefix is null + (and (= n 1) ; prefix + (/= (match-beginning 1) (match-end 1)))) ; non-empty + (let ((font-lock-syntactic-keywords nil)) + (unless (eq 'string (syntax-ppss-context (syntax-ppss))) + (eval-when-compile (string-to-syntax "|"))))) + ;; Otherwise (we're in a non-matching string) the property is + ;; nil, which is OK. + ))) + +(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) + "Syntax table for Python files.") + +(defvar python-dotty-syntax-table + (let ((table (make-syntax-table python-mode-syntax-table))) + (modify-syntax-entry ?. "w" table) + (modify-syntax-entry ?_ "w" table) + table) + "Dotty syntax table for Python files. +It makes underscores and dots word constituent chars.") + + +;;; Indentation + +(defcustom python-indent-offset 4 + "Default indentation offset for Python." + :group 'python + :type 'integer + :safe 'integerp) + +(defcustom python-indent-guess-indent-offset t + "Non-nil tells Python mode to guess `python-indent-offset' value." + :type 'boolean + :group 'python) + +(defvar python-indent-current-level 0 + "Current indentation level `python-indent-line-function' is using.") + +(defvar python-indent-levels '(0) + "Levels of indentation available for `python-indent-line-function'.") + +(defvar python-indent-dedenters '("else" "elif" "except" "finally") + "List of words that should be dedented. +These make `python-indent-calculate-indentation' subtract the value of +`python-indent-offset'.") + +(defun python-indent-guess-indent-offset () + "Guess and set the value for `python-indent-offset' given the current buffer." + (let ((guessed-indentation (save-excursion + (goto-char (point-min)) + (re-search-forward ":\\s-*\n" nil t) + (while (and (not (eobp)) (forward-comment 1))) + (current-indentation)))) + (when (not (equal guessed-indentation 0)) + (setq python-indent-offset guessed-indentation)))) + +(defun python-indent-context (&optional stop) + "Return information on indentation context. +Optional argument STOP serves to stop recursive calls. + +Returns a cons with the form: + +\(STATUS . START) + +Where status can be any of the following symbols: + + * inside-paren: If point in between (), {} or [] + * inside-string: If point is inside a string + * after-backslash: Previous line ends in a backslash + * after-beginning-of-block: Point is after beginning of block + * after-line: Point is after normal line + * no-indent: Point is at beginning of buffer or other special case + +START is the buffer position where the sexp starts." + (save-restriction + (widen) + (let ((ppss (save-excursion (beginning-of-line) (syntax-ppss))) + (start)) + (cons + (cond + ;; Inside a paren + ((setq start (nth 1 ppss)) + 'inside-paren) + ;; Inside string + ((setq start (when (and (nth 3 ppss)) + (nth 8 ppss))) + 'inside-string) + ;; After backslash + ((setq start (when (not (syntax-ppss-context ppss)) + (let ((line-beg-pos (line-beginning-position))) + (when (eq ?\\ (char-before (1- line-beg-pos))) + (- line-beg-pos 2))))) + 'after-backslash) + ;; After beginning of block + ((setq start (save-excursion + (let ((block-regexp (python-rx block-start)) + (block-start-line-end ":[[:space:]]*$")) + (back-to-indentation) + (while (and (forward-comment -1) (not (bobp)))) + (back-to-indentation) + (when (or (python-info-continuation-line-p) + (and (not (looking-at block-regexp)) + (save-excursion + (re-search-forward + block-start-line-end + (line-end-position) t)))) + (while (and (forward-line -1) + (python-info-continuation-line-p) + (not (bobp)))) + (when (not (looking-at block-regexp)) + (forward-line 1))) + (back-to-indentation) + (when (and (looking-at block-regexp) + (or (re-search-forward + block-start-line-end + (line-end-position) t) + (python-info-continuation-line-p))) + (point-marker))))) + 'after-beginning-of-block) + ;; After normal line + ((setq start (save-excursion + (while (and (forward-comment -1) (not (bobp)))) + (while (and (not (back-to-indentation)) + (not (bobp)) + (if (> (nth 0 (syntax-ppss)) 0) + (forward-line -1) + (if (save-excursion + (forward-line -1) + (python-info-line-ends-backslash-p)) + (forward-line -1))))) + (point-marker))) + 'after-line) + ;; Do not indent + (t 'no-indent)) + start)))) + +(defun python-indent-calculate-indentation () + "Calculate correct indentation offset for the current line." + (let* ((indentation-context (python-indent-context)) + (context-status (car indentation-context)) + (context-start (cdr indentation-context))) + (save-restriction + (widen) + (save-excursion + (case context-status + ('no-indent 0) + ('after-beginning-of-block + (goto-char context-start) + (+ (current-indentation) python-indent-offset)) + ('after-line + (- + (save-excursion + (goto-char context-start) + (current-indentation)) + (if (progn + (back-to-indentation) + (looking-at (regexp-opt python-indent-dedenters))) + python-indent-offset + 0))) + ('inside-string + (goto-char context-start) + (current-indentation)) + ('after-backslash + (let* ((block-continuation + (save-excursion + (forward-line -1) + (python-info-block-continuation-line-p))) + (assignment-continuation + (save-excursion + (forward-line -1) + (python-info-assignment-continuation-line-p))) + (indentation (cond (block-continuation + (goto-char block-continuation) + (re-search-forward + (python-rx block-start (* space)) + (line-end-position) t) + (current-column)) + (assignment-continuation + (goto-char assignment-continuation) + (re-search-forward + (python-rx simple-operator) + (line-end-position) t) + (forward-char 1) + (re-search-forward + (python-rx (* space)) + (line-end-position) t) + (current-column)) + (t + (goto-char context-start) + (current-indentation))))) + indentation)) + ('inside-paren + (- + (save-excursion + (goto-char context-start) + (forward-char) + (if (looking-at "[[:space:]]*$") + (+ (current-indentation) python-indent-offset) + (forward-comment 1) + (current-column))) + (if (progn + (back-to-indentation) + (looking-at (regexp-opt '(")" "]" "}")))) + python-indent-offset + 0)))))))) + +(defun python-indent-calculate-levels () + "Calculate `python-indent-levels' and reset `python-indent-current-level'." + (let* ((indentation (python-indent-calculate-indentation)) + (remainder (% indentation python-indent-offset)) + (steps (/ (- indentation remainder) python-indent-offset))) + (setq python-indent-levels '()) + (setq python-indent-levels (cons 0 python-indent-levels)) + (dotimes (step steps) + (setq python-indent-levels + (cons (* python-indent-offset (1+ step)) python-indent-levels))) + (when (not (eq 0 remainder)) + (setq python-indent-levels + (cons (+ (* python-indent-offset steps) remainder) + python-indent-levels))) + (setq python-indent-levels (nreverse python-indent-levels)) + (setq python-indent-current-level (1- (length python-indent-levels))))) + +(defun python-indent-toggle-levels () + "Toggle `python-indent-current-level' over `python-indent-levels'." + (setq python-indent-current-level (1- python-indent-current-level)) + (when (< python-indent-current-level 0) + (setq python-indent-current-level (1- (length python-indent-levels))))) + +(defun python-indent-line (&optional force-toggle) + "Internal implementation of `python-indent-line-function'. + +Uses the offset calculated in +`python-indent-calculate-indentation' and available levels +indicated by the variable `python-indent-levels'. + +When the variable `last-command' is equal to +`indent-for-tab-command' or FORCE-TOGGLE is non-nil: + +* Cycles levels indicated in the variable `python-indent-levels' + by setting the current level in the variable + `python-indent-current-level'. + +When the variable `last-command' is not equal to +`indent-for-tab-command' and FORCE-TOGGLE is nil: + +* calculates possible indentation levels and saves it in the + variable `python-indent-levels'. + +* sets the variable `python-indent-current-level' correctly so + offset is equal to (`nth' `python-indent-current-level' + `python-indent-levels')" + (if (or (and (eq this-command 'indent-for-tab-command) + (eq last-command this-command)) + force-toggle) + (python-indent-toggle-levels) + (python-indent-calculate-levels)) + (beginning-of-line) + (delete-horizontal-space) + (indent-to (nth python-indent-current-level python-indent-levels)) + (save-restriction + (widen) + (let ((closing-block-point (python-info-closing-block))) + (when closing-block-point + (message "Closes %s" (buffer-substring + closing-block-point + (save-excursion + (goto-char closing-block-point) + (line-end-position)))))))) + +(defun python-indent-line-function () + "`indent-line-function' for Python mode. +Internally just calls `python-indent-line'." + (python-indent-line)) + +(defun python-indent-dedent-line () + "Dedent current line." + (interactive "*") + (when (and (not (syntax-ppss-context (syntax-ppss))) + (<= (point-marker) (save-excursion + (back-to-indentation) + (point-marker))) + (> (current-column) 0)) + (python-indent-line t) + t)) + +(defun python-indent-dedent-line-backspace (arg) + "Dedent current line. +Argument ARG is passed to `backward-delete-char-untabify' when +point is not in between the indentation." + (interactive "*p") + (when (not (python-indent-dedent-line)) + (backward-delete-char-untabify arg))) + +(defun python-indent-region (start end) + "Indent a python region automagically. + +Called from a program, START and END specify the region to indent." + (save-excursion + (goto-char end) + (setq end (point-marker)) + (goto-char start) + (or (bolp) (forward-line 1)) + (while (< (point) end) + (or (and (bolp) (eolp)) + (let (word) + (forward-line -1) + (back-to-indentation) + (setq word (current-word)) + (forward-line 1) + (when word + (beginning-of-line) + (delete-horizontal-space) + (indent-to (python-indent-calculate-indentation))))) + (forward-line 1)) + (move-marker end nil))) + +(defun python-indent-shift-left (start end &optional count) + "Shift lines contained in region START END by COUNT columns to the left. + +COUNT defaults to `python-indent-offset'. + +If region isn't active, the current line is shifted. + +The shifted region includes the lines in which START and END lie. + +An error is signaled 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-offset)) + (when (> count 0) + (save-excursion + (goto-char start) + (while (< (point) end) + (if (and (< (current-indentation) count) + (not (looking-at "[ \t]*$"))) + (error "Can't shift all lines enough")) + (forward-line)) + (indent-rigidly start end (- count))))) + +(add-to-list 'debug-ignored-errors "^Can't shift all lines enough") + +(defun python-indent-shift-right (start end &optional count) + "Shift lines contained in region START END by COUNT columns to the left. + +COUNT defaults to `python-indent-offset'. + +If region isn't active, the current line is shifted. + +The shifted region 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-offset)) + (indent-rigidly start end count)) + + +;;; Navigation + +(defvar python-beginning-of-defun-regexp + "^\\(def\\|class\\)[[:space:]]+[[:word:]]+" + "Regular expresion matching beginning of outermost class or function.") + +(defvar python-beginning-of-innermost-defun-regexp + "^[[:space:]]*\\(def\\|class\\)[[:space:]]+[[:word:]]+" + "Regular expresion matching beginning of innermost class or function.") + +(defun python-beginning-of-defun (&optional innermost) + "Move point to the beginning of innermost/outermost def or class. +If INNERMOST is non-nil then move to the beginning of the +innermost definition." + (let ((starting-point (point-marker)) + (nonblank-line-indent) + (defun-indent) + (defun-point) + (regexp (if innermost + python-beginning-of-innermost-defun-regexp + python-beginning-of-defun-regexp))) + (back-to-indentation) + (if (and (not (looking-at "@")) + (not (looking-at regexp))) + (forward-comment -1) + (while (and (not (eobp)) + (forward-line 1) + (not (back-to-indentation)) + (looking-at "@")))) + (when (not (looking-at regexp)) + (re-search-backward regexp nil t)) + (setq nonblank-line-indent (+ (current-indentation) python-indent-offset)) + (setq defun-indent (current-indentation)) + (setq defun-point (point-marker)) + (if (> nonblank-line-indent defun-indent) + (progn + (goto-char defun-point) + (forward-line -1) + (while (and (looking-at "@") + (forward-line -1) + (not (bobp)) + (not (back-to-indentation)))) + (forward-line 1) + (point-marker)) + (if innermost + (python-beginning-of-defun) + (goto-char starting-point) + nil)))) + +(defun python-beginning-of-defun-function () + "Move point to the beginning of outermost def or class. +Returns nil if point is not in a def or class." + (python-beginning-of-defun nil)) + +(defun python-beginning-of-innermost-defun () + "Move point to the beginning of innermost def or class. +Returns nil if point is not in a def or class." + (interactive) + (python-beginning-of-defun t)) + +(defun python-end-of-defun-function () + "Move point to the end of def or class. +Returns nil if point is not in a def or class." + (let ((starting-point (point-marker)) + (defun-regexp (python-rx defun)) + (beg-defun-indent)) + (back-to-indentation) + (if (looking-at "@") + (while (and (not (eobp)) + (forward-line 1) + (not (back-to-indentation)) + (looking-at "@"))) + (while (and (not (bobp)) + (not (progn (back-to-indentation) (current-word))) + (forward-line -1)))) + (when (or (not (equal (current-indentation) 0)) + (string-match defun-regexp (current-word))) + (setq beg-defun-indent (save-excursion + (or (looking-at defun-regexp) + (python-beginning-of-innermost-defun)) + (current-indentation))) + (while (and (forward-line 1) + (not (eobp)) + (or (not (current-word)) + (> (current-indentation) beg-defun-indent)))) + (while (and (forward-comment -1) + (not (bobp)))) + (forward-line 1) + (point-marker)))) + + +;;; Shell integration + +(defvar python-shell-buffer-name "Python" + "Default buffer name for Python interpreter.") + +(defcustom python-shell-interpreter "python" + "Default Python interpreter for shell." + :group 'python + :type 'string + :safe 'stringp) + +(defcustom python-shell-interpreter-args "-i" + "Default arguments for the Python interpreter." + :group 'python + :type 'string + :safe 'stringp) + +(defcustom python-shell-prompt-regexp ">>> " + "Regex matching top\-level input prompt of python shell. +The regex should not contain a caret (^) at the beginning." + :type 'string + :group 'python + :safe 'stringp) + +(defcustom python-shell-prompt-block-regexp "[.][.][.] " + "Regex matching block input prompt of python shell. +The regex should not contain a caret (^) at the beginning." + :type 'string + :group 'python + :safe 'stringp) + +(defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " + "Regex matching pdb input prompt of python shell. +The regex should not contain a caret (^) at the beginning." + :type 'string + :group 'python + :safe 'stringp) + +(defcustom python-shell-compilation-regexp-alist + `((,(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) + (,(rx line-start "> " (group (1+ (not (any "(\"<")))) + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + 1 2)) + "`compilation-error-regexp-alist' for inferior Python." + :type '(alist string) + :group 'python) + +(defun python-shell-get-process-name (dedicated) + "Calculate the appropiate process name for inferior Python process. + +If DEDICATED is t and the variable `buffer-file-name' is non-nil +returns a string with the form +`python-shell-buffer-name'[variable `buffer-file-name'] else +returns the value of `python-shell-buffer-name'. + +After calculating the process name add the buffer name for the +process in the `same-window-buffer-names' list" + (let ((process-name + (if (and dedicated + buffer-file-name) + (format "%s[%s]" python-shell-buffer-name buffer-file-name) + (format "%s" python-shell-buffer-name)))) + (add-to-list 'same-window-buffer-names (purecopy + (format "*%s*" process-name))) + process-name)) + +(defun python-shell-parse-command () + "Calculates the string used to execute the inferior Python process." + (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) + +(defun python-comint-output-filter-function (output) + "Hook run after content is put into comint buffer. +OUTPUT is a string with the contents of the buffer." + (ansi-color-filter-apply output)) + +(defvar inferior-python-mode-current-file nil + "Current file from which a region was sent.") +(make-variable-buffer-local 'inferior-python-mode-current-file) + +(defvar inferior-python-mode-current-temp-file nil + "Current temp file sent to process.") +(make-variable-buffer-local 'inferior-python-mode-current-file) + +(define-derived-mode inferior-python-mode comint-mode "Inferior Python" + "Major mode for Python inferior process." + (set-syntax-table python-mode-syntax-table) + (setq mode-line-process '(":%s")) + (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-pdb-regexp)) + (make-local-variable 'comint-output-filter-functions) + (add-hook 'comint-output-filter-functions + 'python-comint-output-filter-function) + (add-hook 'comint-output-filter-functions + 'python-pdbtrack-comint-output-filter-function) + (set (make-local-variable 'compilation-error-regexp-alist) + python-shell-compilation-regexp-alist) + (compilation-shell-minor-mode 1)) + +(defun run-python (dedicated cmd) + "Run an inferior Python process. + +Input and output via buffer *\\[python-shell-buffer-name]*. + +If there is a process already running in +*\\[python-shell-buffer-name]*, switch to that buffer. + +With argument, allows you to: + + * Define DEDICATED so a dedicated process for the current buffer + is open. + + * Define CMD so you can edit the command used to call the +interpreter (default is value of `python-shell-interpreter' and +arguments defined in `python-shell-interpreter-args'). + +Runs the hook `inferior-python-mode-hook' (after the +`comint-mode-hook' is run). + +\(Type \\[describe-mode] in the process buffer for a list of +commands.)" + (interactive + (if current-prefix-arg + (list + (y-or-n-p "Make dedicated process? ") + (read-string "Run Python: " (python-shell-parse-command))) + (list nil (python-shell-parse-command)))) + (let* ((proc-name (python-shell-get-process-name dedicated)) + (proc-buffer-name (format "*%s*" proc-name))) + (when (not (comint-check-proc proc-buffer-name)) + (let ((cmdlist (split-string-and-unquote cmd))) + (set-buffer + (apply 'make-comint proc-name (car cmdlist) nil + (cdr cmdlist))) + (inferior-python-mode))) + (pop-to-buffer proc-buffer-name)) + dedicated) + +(defun python-shell-get-process () + "Get inferior Python process for current buffer and return it." + (let* ((dedicated-proc-name (python-shell-get-process-name t)) + (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) + (global-proc-name (python-shell-get-process-name nil)) + (global-proc-buffer-name (format "*%s*" global-proc-name)) + (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) + (global-running (comint-check-proc global-proc-buffer-name))) + ;; Always prefer dedicated + (get-buffer-process (or (and dedicated-running dedicated-proc-buffer-name) + (and global-running global-proc-buffer-name))))) + +(defun python-shell-get-or-create-process () + "Get or create an inferior Python process for current buffer and return it." + (let* ((dedicated-proc-name (python-shell-get-process-name t)) + (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) + (global-proc-name (python-shell-get-process-name nil)) + (global-proc-buffer-name (format "*%s*" global-proc-name)) + (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) + (global-running (comint-check-proc global-proc-buffer-name)) + (current-prefix-arg 4)) + (when (and (not dedicated-running) (not global-running)) + (if (call-interactively 'run-python) + (setq dedicated-running t) + (setq global-running t))) + ;; Always prefer dedicated + (get-buffer-process (if dedicated-running + dedicated-proc-buffer-name + global-proc-buffer-name)))) + +(defun python-shell-send-string (string) + "Send STRING to inferior Python process." + (interactive "sPython command: ") + (let ((process (python-shell-get-or-create-process))) + (message (format "Sent: %s..." string)) + (comint-send-string process string) + (when (or (not (string-match "\n$" string)) + (string-match "\n[ \t].*\n?$" string)) + (comint-send-string process "\n")))) + +(defun python-shell-send-region (start end) + "Send the region delimited by START and END to inferior Python process." + (interactive "r") + (let* ((contents (buffer-substring start end)) + (current-file (buffer-file-name)) + (process (python-shell-get-or-create-process)) + (temp-file (make-temp-file "py"))) + (with-temp-file temp-file + (insert contents) + (delete-trailing-whitespace) + (goto-char (point-min)) + (message (format "Sent: %s..." + (buffer-substring (point-min) + (line-end-position))))) + (with-current-buffer (process-buffer process) + (setq inferior-python-mode-current-file current-file) + (setq inferior-python-mode-current-temp-file temp-file)) + (comint-send-string process (format "execfile(r'%s')\n" temp-file)))) + +(defun python-shell-send-buffer () + "Send the entire buffer to inferior Python process." + (interactive) + (save-restriction + (widen) + (python-shell-send-region (point-min) (point-max)))) + +(defun python-shell-send-defun (arg) + "Send the (inner|outer)most def or class to inferior Python process. +When argument ARG is non-nil sends the innermost defun." + (interactive "P") + (save-excursion + (python-shell-send-region (progn + (or (if arg + (python-beginning-of-innermost-defun) + (python-beginning-of-defun-function)) + (progn (beginning-of-line) (point-marker)))) + (progn + (or (python-end-of-defun-function) + (progn (end-of-line) (point-marker))))))) + +(defun python-shell-send-file (file-name) + "Send FILE-NAME to inferior Python process." + (interactive "fFile to send: ") + (comint-send-string + (python-shell-get-or-create-process) + (format "execfile('%s')\n" (expand-file-name file-name)))) + +(defun python-shell-switch-to-shell () + "Switch to inferior Python process buffer." + (interactive) + (pop-to-buffer (process-buffer (python-shell-get-or-create-process)) t)) + + +;;; Shell completion + +(defvar python-shell-completion-setup-code + "try: + import readline +except ImportError: + def __COMPLETER_all_completions(text): [] +else: + import rlcompleter + readline.set_completer(rlcompleter.Completer().complete) + def __COMPLETER_all_completions(text): + import sys + completions = [] + try: + i = 0 + while True: + res = readline.get_completer()(text, i) + if not res: break + i += 1 + completions.append(res) + except NameError: + pass + return completions" + "Code used to setup completion in inferior Python processes.") + +(defvar python-shell-completion-strings-code + "';'.join(__COMPLETER_all_completions('''%s'''))\n" + "Python code used to get a string of completions separated by semicolons.") + +(defun python-shell-completion-setup () + "Send `python-shell-completion-setup-code' to inferior Python process. +Also binds to `python-shell-complete-or-indent' in the +`inferior-python-mode-map' and adds +`python-shell-completion-complete-at-point' to the +`comint-dynamic-complete-functions' list. +It is specially designed to be added to the +`inferior-python-mode-hook'." + (when python-shell-completion-setup-code + (let ((temp-file (make-temp-file "py")) + (process (get-buffer-process (current-buffer)))) + (with-temp-file temp-file + (insert python-shell-completion-setup-code) + (delete-trailing-whitespace) + (goto-char (point-min))) + (comint-send-string process + (format "execfile(r'%s')\n" temp-file)) + (message (format "Completion setup code sent."))) + (add-to-list (make-local-variable + 'comint-dynamic-complete-functions) + 'python-shell-completion-complete-at-point) + (define-key inferior-python-mode-map (kbd "") + 'python-shell-completion-complete-or-indent))) + +(defun python-shell-completion-complete-at-point () + "Perform completion at point in inferior Python process." + (interactive) + (when (and comint-last-prompt-overlay + (> (point-marker) (overlay-end comint-last-prompt-overlay))) + (let* ((process (get-buffer-process (current-buffer))) + (input (comint-word (current-word))) + (completions (when input + (delete-region (point-marker) + (progn + (forward-char (- (length input))) + (point-marker))) + (process-send-string + process + (format + python-shell-completion-strings-code input)) + (accept-process-output process) + (save-excursion + (re-search-backward comint-prompt-regexp + comint-last-input-end t) + (split-string + (buffer-substring-no-properties + (point-marker) comint-last-input-end) + ";\\|\"\\|'\\|(" t)))) + (completion (when completions (try-completion input completions)))) + (when completions + (save-excursion + (forward-line -1) + (kill-line 1))) + (cond ((eq completion t) + (when input (insert input))) + ((null completion) + (when input (insert input)) + (message "Can't find completion for \"%s\"" input) + (ding)) + ((not (string= input completion)) + (insert completion)) + (t + (message "Making completion list...") + (when input (insert input)) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions)))))))) + + +(defun python-shell-completion-complete-or-indent () + "Complete or indent depending on the context. +If content before pointer is all whitespace indent. If not try to +complete." + (interactive) + (if (string-match "^[[:space:]]*$" + (buffer-substring (comint-line-beginning-position) + (point-marker))) + (indent-for-tab-command) + (comint-dynamic-complete))) + +(add-hook 'inferior-python-mode-hook + #'python-shell-completion-setup) + + +;;; PDB Track integration + +(defvar python-pdbtrack-stacktrace-info-regexp + "> %s(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "Regexp matching stacktrace information. +It is used to extract the current line and module beign +inspected. +The regexp should not start with a caret (^) and can contain a +string placeholder (\%s) which is replaced with the filename +beign inspected (so other files in the debugging process are not +opened)") + +(defvar python-pdbtrack-tracking-buffers '() + "Alist containing elements of form (# . #). +The car of each element of the alist is the tracking buffer and +the cdr is the tracked buffer.") + +(defun python-pdbtrack-get-or-add-tracking-buffers () + "Get/Add a tracked buffer for the current buffer. +Internally it uses the `python-pdbtrack-tracking-buffers' alist. +Returns a cons with the form: + * (# . #< tracked buffer>)." + (or + (assq (current-buffer) python-pdbtrack-tracking-buffers) + (let* ((file (with-current-buffer (current-buffer) + (or inferior-python-mode-current-file + inferior-python-mode-current-temp-file))) + (tracking-buffers + `(,(current-buffer) . + ,(or (get-file-buffer file) + (find-file-noselect file))))) + (set-buffer (cdr tracking-buffers)) + (python-mode) + (set-buffer (car tracking-buffers)) + (setq python-pdbtrack-tracking-buffers + (cons tracking-buffers python-pdbtrack-tracking-buffers)) + tracking-buffers))) + +(defun python-pdbtrack-comint-output-filter-function (output) + "Move overlay arrow to current pdb line in tracked buffer. +Argument OUTPUT is a string with the output from the comint process." + (when (not (string= output "")) + (let ((full-output (ansi-color-filter-apply + (buffer-substring comint-last-input-end + (point-max))))) + (if (string-match python-shell-prompt-pdb-regexp full-output) + (let* ((tracking-buffers (python-pdbtrack-get-or-add-tracking-buffers)) + (line-num + (save-excursion + (string-match + (format python-pdbtrack-stacktrace-info-regexp + (regexp-quote + inferior-python-mode-current-temp-file)) + full-output) + (string-to-number (or (match-string-no-properties 1 full-output) "")))) + (tracked-buffer-window (get-buffer-window (cdr tracking-buffers))) + (tracked-buffer-line-pos)) + (when line-num + (with-current-buffer (cdr tracking-buffers) + (set (make-local-variable 'overlay-arrow-string) "=>") + (set (make-local-variable 'overlay-arrow-position) (make-marker)) + (setq tracked-buffer-line-pos (progn + (goto-char (point-min)) + (forward-line (1- line-num)) + (point-marker))) + (when tracked-buffer-window + (set-window-point tracked-buffer-window tracked-buffer-line-pos)) + (set-marker overlay-arrow-position tracked-buffer-line-pos))) + (pop-to-buffer (cdr tracking-buffers)) + (switch-to-buffer-other-window (car tracking-buffers))) + (let ((tracking-buffers (assq (current-buffer) + python-pdbtrack-tracking-buffers))) + (when tracking-buffers + (if inferior-python-mode-current-file + (with-current-buffer (cdr tracking-buffers) + (set-marker overlay-arrow-position nil)) + (kill-buffer (cdr tracking-buffers))) + (setq python-pdbtrack-tracking-buffers + (assq-delete-all (current-buffer) + python-pdbtrack-tracking-buffers))))))) + output) + + +;;; Symbol completion + +(defun python-completion-complete-at-point () + "Complete current symbol at point. +For this to work the best as possible you should call +`python-shell-send-buffer' from time to time so context in +inferior python process is updated properly." + (interactive) + (let ((process (python-shell-get-process))) + (if (not process) + (error "Completion needs an inferior Python process running.") + (let* ((input (when (comint-word (current-word)) + (with-syntax-table python-dotty-syntax-table + (buffer-substring (point-marker) + (save-excursion + (forward-word -1) + (point-marker)))))) + (completions (when input + (delete-region (point-marker) + (progn + (forward-char (- (length input))) + (point-marker))) + (process-send-string + process + (format + python-shell-completion-strings-code input)) + (accept-process-output process) + (with-current-buffer (process-buffer process) + (save-excursion + (re-search-backward comint-prompt-regexp + comint-last-input-end t) + (split-string + (buffer-substring-no-properties + (point-marker) comint-last-input-end) + ";\\|\"\\|'\\|(" t))))) + (completion (when completions + (try-completion input completions)))) + (with-current-buffer (process-buffer process) + (save-excursion + (forward-line -1) + (kill-line 1))) + (when completions + (cond ((eq completion t) + (insert input)) + ((null completion) + (insert input) + (message "Can't find completion for \"%s\"" input) + (ding)) + ((not (string= input completion)) + (insert completion)) + (t + (message "Making completion list...") + (insert input) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions)))))))))) + +(add-to-list 'debug-ignored-errors "^Completion needs an inferior Python process running.") + + +;;; Fill paragraph + +(defun python-fill-paragraph-function (&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") + (save-excursion + (back-to-indentation) + (cond + ;; Comments + ((fill-comment-paragraph justify)) + ;; Docstrings + ((save-excursion (skip-chars-forward "\"'uUrR") + (nth 3 (syntax-ppss))) + (let ((marker (point-marker)) + (string-start-marker + (progn + (skip-chars-forward "\"'uUrR") + (goto-char (nth 8 (syntax-ppss))) + (skip-chars-forward "\"'uUrR") + (point-marker))) + (reg-start (line-beginning-position)) + (string-end-marker + (progn + (while (nth 3 (syntax-ppss)) (goto-char (1+ (point-marker)))) + (skip-chars-backward "\"'") + (point-marker))) + (reg-end (line-end-position)) + (fill-paragraph-function)) + (save-restriction + (narrow-to-region reg-start reg-end) + (save-excursion + (goto-char string-start-marker) + (delete-region (point-marker) (progn + (skip-syntax-forward "> ") + (point-marker))) + (goto-char string-end-marker) + (delete-region (point-marker) (progn + (skip-syntax-backward "> ") + (point-marker))) + (save-excursion + (goto-char marker) + (fill-paragraph justify)) + ;; If there is a newline in the docstring lets put triple + ;; quote in it's own line to follow pep 8 + (when (save-excursion + (re-search-backward "\n" string-start-marker t)) + (newline) + (newline-and-indent)) + (fill-paragraph justify)))) t) + ;; Decorators + ((equal (char-after (save-excursion + (back-to-indentation) + (point-marker))) ?@) t) + ;; Parens + ((or (> (nth 0 (syntax-ppss)) 0) + (looking-at (python-rx open-paren)) + (save-excursion + (skip-syntax-forward "^(" (line-end-position)) + (looking-at (python-rx open-paren)))) + (save-restriction + (narrow-to-region (progn + (while (> (nth 0 (syntax-ppss)) 0) + (goto-char (1- (point-marker)))) + (point-marker) + (line-beginning-position)) + (progn + (when (not (> (nth 0 (syntax-ppss)) 0)) + (end-of-line) + (when (not (> (nth 0 (syntax-ppss)) 0)) + (skip-syntax-backward "^)"))) + (while (> (nth 0 (syntax-ppss)) 0) + (goto-char (1+ (point-marker)))) + (point-marker))) + (let ((paragraph-start "\f\\|[ \t]*$") + (paragraph-separate ",") + (fill-paragraph-function)) + (goto-char (point-min)) + (fill-paragraph justify)) + (while (not (eobp)) + (forward-line 1) + (python-indent-line) + (goto-char (line-end-position)))) t) + (t t)))) + + +;;; Eldoc + +(defvar python-eldoc-setup-code + "def __PYDOC_get_help(obj): + try: + import pydoc + obj = eval(obj, globals()) + return pydoc.getdoc(obj) + except: + return ''" + "Python code to setup documentation retrieval.") + +(defvar python-eldoc-string-code + "print __PYDOC_get_help('''%s''')\n" + "Python code used to get a string with the documentation of an object.") + +(defun python-eldoc-setup () + "Send `python-eldoc-setup-code' to inferior Python process. +It is specially designed to be added to the +`inferior-python-mode-hook'." + (when python-eldoc-setup-code + (let ((temp-file (make-temp-file "py"))) + (with-temp-file temp-file + (insert python-eldoc-setup-code) + (delete-trailing-whitespace) + (goto-char (point-min))) + (comint-send-string (get-buffer-process (current-buffer)) + (format "execfile(r'%s')\n" temp-file)) + (message (format "Completion setup code sent."))))) + +(defun python-eldoc-function () + "`eldoc-documentation-function' for Python. +For this to work the best as possible you should call +`python-shell-send-buffer' from time to time so context in +inferior python process is updated properly." + (interactive) + (let ((process (python-shell-get-process))) + (if (not process) + "Eldoc needs an inferior Python process running." + (let* ((current-defun (python-info-current-defun)) + (input (with-syntax-table python-dotty-syntax-table + (if (not current-defun) + (current-word) + (concat current-defun "." (current-word))))) + (ppss (syntax-ppss)) + (help (when (and input + (not (string= input (concat current-defun "."))) + (eq nil (nth 3 ppss)) + (eq nil (nth 4 ppss))) + (when (string-match (concat + (regexp-quote (concat current-defun ".")) + "self\\.") input) + (with-temp-buffer + (insert input) + (goto-char (point-min)) + (forward-word) + (forward-char) + (delete-region (point-marker) (search-forward "self.")) + (setq input (buffer-substring (point-min) (point-max))))) + (process-send-string + process (format python-eldoc-string-code input)) + (accept-process-output process) + (with-current-buffer (process-buffer process) + (when comint-last-prompt-overlay + (save-excursion + (goto-char comint-last-input-end) + (re-search-forward comint-prompt-regexp + (line-end-position) t) + (buffer-substring-no-properties + (point-marker) + (overlay-start comint-last-prompt-overlay)))))))) + (with-current-buffer (process-buffer process) + (when comint-last-prompt-overlay + (delete-region comint-last-input-end + (overlay-start comint-last-prompt-overlay)))) + (when (and help + (not (string= help "\n"))) + help))))) + +(add-hook 'inferior-python-mode-hook + #'python-eldoc-setup) + + +;;; Misc helpers + +(defun python-info-current-defun () + "Return name of surrounding function with Python compatible dotty syntax. +This function is compatible to be used as +`add-log-current-defun-function' since it returns nil if point is +not inside a defun." + (let ((names '())) + (save-restriction + (widen) + (save-excursion + (beginning-of-line) + (when (not (>= (current-indentation) python-indent-offset)) + (while (and (not (eobp)) (forward-comment 1)))) + (while (and (not (equal 0 (current-indentation))) + (python-beginning-of-innermost-defun)) + (back-to-indentation) + (looking-at "\\(?:def\\|class\\) +\\([^(]+\\)[^:]+:\\s-*\n") + (setq names (cons (match-string-no-properties 1) names))))) + (when names + (mapconcat (lambda (string) string) names ".")))) + +(defun python-info-closing-block () + "Return the point of the block that the current line closes." + (let ((closing-word (save-excursion + (back-to-indentation) + (current-word))) + (indentation (current-indentation))) + (when (member closing-word python-indent-dedenters) + (save-excursion + (forward-line -1) + (while (and (> (current-indentation) indentation) + (not (bobp)) + (not (back-to-indentation)) + (forward-line -1))) + (back-to-indentation) + (cond + ((not (equal indentation (current-indentation))) nil) + ((string= closing-word "elif") + (when (member (current-word) '("if" "elif")) + (point-marker))) + ((string= closing-word "else") + (when (member (current-word) '("if" "elif" "except" "for" "while")) + (point-marker))) + ((string= closing-word "except") + (when (member (current-word) '("try")) + (point-marker))) + ((string= closing-word "finally") + (when (member (current-word) '("except" "else")) + (point-marker)))))))) + +(defun python-info-line-ends-backslash-p () + "Return non-nil if current line ends with backslash." + (string= (or (ignore-errors + (buffer-substring + (line-end-position) + (- (line-end-position) 1))) "") "\\")) + +(defun python-info-continuation-line-p () + "Return non-nil if current line is continuation of another." + (or (python-info-line-ends-backslash-p) + (string-match ",[[:space:]]*$" (buffer-substring + (line-beginning-position) + (line-end-position))) + (save-excursion + (let ((innermost-paren (progn + (goto-char (line-end-position)) + (nth 1 (syntax-ppss))))) + (when (and innermost-paren + (and (<= (line-beginning-position) innermost-paren) + (>= (line-end-position) innermost-paren))) + (goto-char innermost-paren) + (looking-at (python-rx open-paren (* space) line-end))))) + (save-excursion + (back-to-indentation) + (nth 1 (syntax-ppss))))) + +(defun python-info-block-continuation-line-p () + "Return non-nil if current line is a continuation of a block." + (save-excursion + (while (and (not (bobp)) + (python-info-continuation-line-p)) + (forward-line -1)) + (forward-line 1) + (back-to-indentation) + (when (looking-at (python-rx block-start)) + (point-marker)))) + +(defun python-info-assignment-continuation-line-p () + "Return non-nil if current line is a continuation of an assignment." + (save-excursion + (while (and (not (bobp)) + (python-info-continuation-line-p)) + (forward-line -1)) + (forward-line 1) + (back-to-indentation) + (when (and (not (looking-at (python-rx block-start))) + (save-excursion + (and (re-search-forward (python-rx not-simple-operator + assignment-operator + not-simple-operator) + (line-end-position) t) + (not (syntax-ppss-context (syntax-ppss)))))) + (point-marker)))) + + +;;;###autoload +(define-derived-mode python-mode fundamental-mode "Python" + "A major mode for editing Python files." + (set (make-local-variable 'tab-width) 8) + (set (make-local-variable 'indent-tabs-mode) nil) + + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-start-skip) "#+\\s-*") + + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'parse-sexp-ignore-comments) t) + + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords + nil nil nil nil + (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords))) + + (set (make-local-variable 'indent-line-function) #'python-indent-line-function) + (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-function) + + (set (make-local-variable 'beginning-of-defun-function) + #'python-beginning-of-defun-function) + (set (make-local-variable 'end-of-defun-function) + #'python-end-of-defun-function) + + (add-hook 'completion-at-point-functions + 'python-completion-complete-at-point nil 'local) + + (set (make-local-variable 'add-log-current-defun-function) + #'python-info-current-defun) + + (set (make-local-variable 'eldoc-documentation-function) + #'python-eldoc-function) + + (add-to-list 'hs-special-modes-alist + `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" + ,(lambda (arg) + (python-end-of-defun-function)) nil)) + + (set (make-local-variable 'outline-regexp) + (python-rx (* space) block-start)) + (set (make-local-variable 'outline-heading-end-regexp) ":\\s-*\n") + (set (make-local-variable 'outline-level) + #'(lambda () + "`outline-level' function for Python mode." + (1+ (/ (current-indentation) python-indent-offset)))) + + (when python-indent-guess-indent-offset + (python-indent-guess-indent-offset))) + + +(provide 'python) +;;; python.el ends here -- cgit v1.2.1 From 6ac2041b868fb13d2838a83a1e7bd565f9dcd81c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:52 -0300 Subject: Cleanup prompts when sending region to shell. --- lisp/progmodes/python.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b281c01b7d9..122b44a9dde 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1002,7 +1002,8 @@ commands.)" (let* ((contents (buffer-substring start end)) (current-file (buffer-file-name)) (process (python-shell-get-or-create-process)) - (temp-file (make-temp-file "py"))) + (temp-file (make-temp-file "py")) + (process-buffer (process-buffer process))) (with-temp-file temp-file (insert contents) (delete-trailing-whitespace) @@ -1012,7 +1013,11 @@ commands.)" (line-end-position))))) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file current-file) - (setq inferior-python-mode-current-temp-file temp-file)) + (setq inferior-python-mode-current-temp-file temp-file) + (delete-region (save-excursion + (move-to-column 0) + (point-marker)) + (line-end-position))) (comint-send-string process (format "execfile(r'%s')\n" temp-file)))) (defun python-shell-send-buffer () @@ -1146,7 +1151,6 @@ It is specially designed to be added to the (display-completion-list (all-completions input completions)))))))) - (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. If content before pointer is all whitespace indent. If not try to -- cgit v1.2.1 From 66b0b492bcb40a7d0675ed3471da92aae4bde0ba Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:52 -0300 Subject: Generalized use of python-shell-send-file with cleanup of prompts. --- lisp/progmodes/python.el | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 122b44a9dde..6458c359355 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1002,8 +1002,7 @@ commands.)" (let* ((contents (buffer-substring start end)) (current-file (buffer-file-name)) (process (python-shell-get-or-create-process)) - (temp-file (make-temp-file "py")) - (process-buffer (process-buffer process))) + (temp-file (make-temp-file "py"))) (with-temp-file temp-file (insert contents) (delete-trailing-whitespace) @@ -1013,12 +1012,8 @@ commands.)" (line-end-position))))) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file current-file) - (setq inferior-python-mode-current-temp-file temp-file) - (delete-region (save-excursion - (move-to-column 0) - (point-marker)) - (line-end-position))) - (comint-send-string process (format "execfile(r'%s')\n" temp-file)))) + (setq inferior-python-mode-current-temp-file temp-file)) + (python-shell-send-file temp-file process))) (defun python-shell-send-buffer () "Send the entire buffer to inferior Python process." @@ -1041,12 +1036,19 @@ When argument ARG is non-nil sends the innermost defun." (or (python-end-of-defun-function) (progn (end-of-line) (point-marker))))))) -(defun python-shell-send-file (file-name) +(defun python-shell-send-file (file-name &optional process) "Send FILE-NAME to inferior Python process." (interactive "fFile to send: ") - (comint-send-string - (python-shell-get-or-create-process) - (format "execfile('%s')\n" (expand-file-name file-name)))) + (let ((process (or process (python-shell-get-or-create-process)))) + (accept-process-output process) + (with-current-buffer (process-buffer process) + (delete-region (save-excursion + (move-to-column 0) + (point-marker)) + (line-end-position))) + (comint-send-string + process + (format "execfile('%s')\n" (expand-file-name file-name))))) (defun python-shell-switch-to-shell () "Switch to inferior Python process buffer." @@ -1098,8 +1100,7 @@ It is specially designed to be added to the (insert python-shell-completion-setup-code) (delete-trailing-whitespace) (goto-char (point-min))) - (comint-send-string process - (format "execfile(r'%s')\n" temp-file)) + (python-shell-send-file temp-file process) (message (format "Completion setup code sent."))) (add-to-list (make-local-variable 'comint-dynamic-complete-functions) @@ -1423,8 +1424,7 @@ It is specially designed to be added to the (insert python-eldoc-setup-code) (delete-trailing-whitespace) (goto-char (point-min))) - (comint-send-string (get-buffer-process (current-buffer)) - (format "execfile(r'%s')\n" temp-file)) + (python-shell-send-file temp-file (get-buffer-process (current-buffer))) (message (format "Completion setup code sent."))))) (defun python-eldoc-function () -- cgit v1.2.1 From 79dafa51ba09e8a5fc8f48b23b440388dd3596e8 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:53 -0300 Subject: python-shell-get-or-create-process preserves current buffer. --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6458c359355..37a4d029c0f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -970,7 +970,8 @@ commands.)" (defun python-shell-get-or-create-process () "Get or create an inferior Python process for current buffer and return it." - (let* ((dedicated-proc-name (python-shell-get-process-name t)) + (let* ((old-buffer (current-buffer)) + (dedicated-proc-name (python-shell-get-process-name t)) (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) (global-proc-name (python-shell-get-process-name nil)) (global-proc-buffer-name (format "*%s*" global-proc-name)) @@ -982,6 +983,7 @@ commands.)" (setq dedicated-running t) (setq global-running t))) ;; Always prefer dedicated + (switch-to-buffer old-buffer) (get-buffer-process (if dedicated-running dedicated-proc-buffer-name global-proc-buffer-name)))) -- cgit v1.2.1 From 954aa7bdb84f2b84d3ad3c248712c09a710582fa Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:53 -0300 Subject: Enhanced python-indent-guess-indent-offset logic. --- lisp/progmodes/python.el | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 37a4d029c0f..abe92dd43dd 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -401,14 +401,25 @@ These make `python-indent-calculate-indentation' subtract the value of `python-indent-offset'.") (defun python-indent-guess-indent-offset () - "Guess and set the value for `python-indent-offset' given the current buffer." - (let ((guessed-indentation (save-excursion - (goto-char (point-min)) - (re-search-forward ":\\s-*\n" nil t) - (while (and (not (eobp)) (forward-comment 1))) - (current-indentation)))) - (when (not (equal guessed-indentation 0)) - (setq python-indent-offset guessed-indentation)))) + "Guess and set `python-indent-offset' for the current buffer." + (save-excursion + (let ((found-block)) + (while (and (not found-block) + (re-search-forward + (python-rx line-start block-start) nil t)) + (when (not (syntax-ppss-context (syntax-ppss))) + (setq found-block t))) + (if (not found-block) + (message "Can't guess python-indent-offset, using defaults: %s" + python-indent-offset) + (while (and (progn + (goto-char (line-end-position)) + (python-info-continuation-line-p)) + (not (eobp))) + (forward-line 1)) + (forward-line 1) + (forward-comment 1) + (setq python-indent-offset (current-indentation)))))) (defun python-indent-context (&optional stop) "Return information on indentation context. -- cgit v1.2.1 From 183f9296f1cf873b5aef9253f0def74e5d6d513e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:53 -0300 Subject: Fixed backspace behavior for delete-selection-mode. --- lisp/progmodes/python.el | 1 + 1 file changed, 1 insertion(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index abe92dd43dd..26b13afe2ed 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -658,6 +658,7 @@ point is not in between the indentation." (interactive "*p") (when (not (python-indent-dedent-line)) (backward-delete-char-untabify arg))) +(put 'python-indent-dedent-line-backspace 'delete-selection 'supersede) (defun python-indent-region (start end) "Indent a python region automagically. -- cgit v1.2.1 From bbac1eb8aba7459dc90e1f8bb21e603d16779077 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:54 -0300 Subject: python-indent-guess-indent-offset improvements --- lisp/progmodes/python.el | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 26b13afe2ed..e4019373362 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -402,24 +402,31 @@ These make `python-indent-calculate-indentation' subtract the value of (defun python-indent-guess-indent-offset () "Guess and set `python-indent-offset' for the current buffer." - (save-excursion - (let ((found-block)) - (while (and (not found-block) - (re-search-forward - (python-rx line-start block-start) nil t)) - (when (not (syntax-ppss-context (syntax-ppss))) - (setq found-block t))) - (if (not found-block) - (message "Can't guess python-indent-offset, using defaults: %s" - python-indent-offset) - (while (and (progn - (goto-char (line-end-position)) - (python-info-continuation-line-p)) - (not (eobp))) - (forward-line 1)) - (forward-line 1) - (forward-comment 1) - (setq python-indent-offset (current-indentation)))))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((found-block)) + (while (and (not found-block) + (re-search-forward + (python-rx line-start block-start) nil t)) + (when (and (not (syntax-ppss-context (syntax-ppss))) + (progn + (goto-char (line-end-position)) + (forward-comment -1) + (eq ?: (char-before)))) + (setq found-block t))) + (if (not found-block) + (message "Can't guess python-indent-offset, using defaults: %s" + python-indent-offset) + (while (and (progn + (goto-char (line-end-position)) + (python-info-continuation-line-p)) + (not (eobp))) + (forward-line 1)) + (forward-line 1) + (forward-comment 1) + (setq python-indent-offset (current-indentation))))))) (defun python-indent-context (&optional stop) "Return information on indentation context. -- cgit v1.2.1 From 13d1a42edb24bec24ff85f85f3fce686cd476e72 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:54 -0300 Subject: Fixed indentation inside parens when comments are around --- lisp/progmodes/python.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e4019373362..3cdfb43e35f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -568,7 +568,12 @@ START is the buffer position where the sexp starts." (save-excursion (goto-char context-start) (forward-char) - (if (looking-at "[[:space:]]*$") + (save-restriction + (narrow-to-region + (line-beginning-position) + (line-end-position)) + (forward-comment 1)) + (if (looking-at "$") (+ (current-indentation) python-indent-offset) (forward-comment 1) (current-column))) -- cgit v1.2.1 From b962ebad98b9dff00b2c945afe353a6820e40bce Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:54 -0300 Subject: Modified autocompletion code setup Now it is compatible with python 2.x and python 3.x --- lisp/progmodes/python.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3cdfb43e35f..9f14bef5258 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1065,7 +1065,8 @@ When argument ARG is non-nil sends the innermost defun." (defun python-shell-send-file (file-name &optional process) "Send FILE-NAME to inferior Python process." (interactive "fFile to send: ") - (let ((process (or process (python-shell-get-or-create-process)))) + (let ((process (or process (python-shell-get-or-create-process))) + (full-file-name (expand-file-name file-name))) (accept-process-output process) (with-current-buffer (process-buffer process) (delete-region (save-excursion @@ -1074,7 +1075,9 @@ When argument ARG is non-nil sends the innermost defun." (line-end-position))) (comint-send-string process - (format "execfile('%s')\n" (expand-file-name file-name))))) + (format + "with open('%s') as __pyfile: exec(compile(__pyfile.read(), '%s', 'exec'))\n\n" + full-file-name full-file-name)))) (defun python-shell-switch-to-shell () "Switch to inferior Python process buffer." -- cgit v1.2.1 From df700cc99edbf1a51f78a52903cd6a9065121e22 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:55 -0300 Subject: Fixed infinite while loop in python-info-current-defun Was caused when a beginning of defun was in the beginning of buffer because python-beginning-of-innermost-defun never reached the real start when (bobp) --- lisp/progmodes/python.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 9f14bef5258..250f4812a33 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -786,7 +786,8 @@ innermost definition." (forward-line -1) (not (bobp)) (not (back-to-indentation)))) - (forward-line 1) + (unless (bobp) + (forward-line 1)) (point-marker)) (if innermost (python-beginning-of-defun) -- cgit v1.2.1 From 69bab1deb3e467716f5ca0d80200563b46ebc89c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:55 -0300 Subject: Do not indent at the beginning of buffer --- lisp/progmodes/python.el | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 250f4812a33..9a55f337996 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -452,6 +452,9 @@ START is the buffer position where the sexp starts." (start)) (cons (cond + ;; Beginning of buffer + ((bobp) + 'no-indent) ;; Inside a paren ((setq start (nth 1 ppss)) 'inside-paren) -- cgit v1.2.1 From 19b122e44ec87cb5e19247049b1088f60f6a72ff Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:56 -0300 Subject: Do not indent at the beginning of buffer fix --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 9a55f337996..3f363f960ed 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -453,7 +453,9 @@ START is the buffer position where the sexp starts." (cons (cond ;; Beginning of buffer - ((bobp) + ((save-excursion + (goto-char (line-beginning-position)) + (bobp)) 'no-indent) ;; Inside a paren ((setq start (nth 1 ppss)) -- cgit v1.2.1 From 4e531f7a7e81c6b820c1ff3d9624a70534c8003e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:56 -0300 Subject: Documentation fixes --- lisp/progmodes/python.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3f363f960ed..ad2d47b2b24 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -51,10 +51,10 @@ ;; block of code of your current buffer in an inferior Python process. ;; Shell completion: hitting tab will try to complete the current -;; word. Shell completion is implemented in a manner that if you +;; word. Shell completion is implemented in a manner that if you ;; change the `python-shell-interpreter' to any other (for example ;; IPython) it should be easy to integrate another way to calculate -;; completions. You just need to especify your custom +;; completions. You just need to especify your custom ;; `python-shell-completion-setup-code' and ;; `python-shell-completion-strings-code' @@ -62,12 +62,12 @@ ;; call to pdb (or ipdb) it will prompt the block of code and will ;; follow the execution of pdb marking the current line with an arrow. -;; Symbol completion: you can complete the symbol at point. It uses +;; Symbol completion: you can complete the symbol at point. It uses ;; the shell completion in background so you should run ;; `python-shell-send-buffer' from time to time to get better results. ;; Eldoc: returns documentation for object at point by using the -;; inferior python subprocess to inspect its documentation. As you +;; inferior python subprocess to inspect its documentation. As you ;; might guessed you should run `python-shell-send-buffer' from time ;; to time to get better results too. @@ -193,7 +193,7 @@ ">>=" "<<=" "&=" "^=" "|="))))) (defmacro python-rx (&rest regexps) - "Python mode especialized rx macro which supports common python named regexps." + "Python mode especialized rx macro which supports common python named REGEXPS." (let ((rx-constituents (append python-rx-constituents rx-constituents))) (cond ((null regexps) (error "No regexp")) @@ -1069,7 +1069,7 @@ When argument ARG is non-nil sends the innermost defun." (progn (end-of-line) (point-marker))))))) (defun python-shell-send-file (file-name &optional process) - "Send FILE-NAME to inferior Python process." + "Send FILE-NAME to inferior Python PROCESS." (interactive "fFile to send: ") (let ((process (or process (python-shell-get-or-create-process))) (full-file-name (expand-file-name file-name))) @@ -1295,7 +1295,7 @@ inferior python process is updated properly." (interactive) (let ((process (python-shell-get-process))) (if (not process) - (error "Completion needs an inferior Python process running.") + (error "Completion needs an inferior Python process running") (let* ((input (when (comint-word (current-word)) (with-syntax-table python-dotty-syntax-table (buffer-substring (point-marker) @@ -1351,7 +1351,8 @@ inferior python process is updated properly." "`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." +the string's indentation. +Optional argument JUSTIFY defines if the paragraph should be justified." (interactive "P") (save-excursion (back-to-indentation) -- cgit v1.2.1 From 13d914ed50eeef89288f143dc2b7a084dbfd9079 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:56 -0300 Subject: Fixed shell region sending --- lisp/progmodes/python.el | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ad2d47b2b24..737730d7823 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1018,11 +1018,12 @@ commands.)" dedicated-proc-buffer-name global-proc-buffer-name)))) -(defun python-shell-send-string (string) +(defun python-shell-send-string (string &optional process) "Send STRING to inferior Python process." (interactive "sPython command: ") - (let ((process (python-shell-get-or-create-process))) - (message (format "Sent: %s..." string)) + (let ((process (or process (python-shell-get-or-create-process)))) + (when (called-interactively-p) + (message (format "Sent: %s..." string))) (comint-send-string process string) (when (or (not (string-match "\n$" string)) (string-match "\n[ \t].*\n?$" string)) @@ -1073,17 +1074,11 @@ When argument ARG is non-nil sends the innermost defun." (interactive "fFile to send: ") (let ((process (or process (python-shell-get-or-create-process))) (full-file-name (expand-file-name file-name))) - (accept-process-output process) - (with-current-buffer (process-buffer process) - (delete-region (save-excursion - (move-to-column 0) - (point-marker)) - (line-end-position))) - (comint-send-string - process + (python-shell-send-string (format - "with open('%s') as __pyfile: exec(compile(__pyfile.read(), '%s', 'exec'))\n\n" - full-file-name full-file-name)))) + "__pyfile = open('%s'); exec(compile(__pyfile.read(), '%s', 'exec')); __pyfile.close()" + full-file-name full-file-name) + process))) (defun python-shell-switch-to-shell () "Switch to inferior Python process buffer." -- cgit v1.2.1 From 3d6913c7d1737ffbe4163cf0f8a19c4d09f2dbc7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:57 -0300 Subject: Fixes to shell completion at point --- lisp/progmodes/python.el | 79 ++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 39 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 737730d7823..b9cbfdd3b4d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1141,46 +1141,47 @@ It is specially designed to be added to the (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." (interactive) - (when (and comint-last-prompt-overlay - (> (point-marker) (overlay-end comint-last-prompt-overlay))) - (let* ((process (get-buffer-process (current-buffer))) - (input (comint-word (current-word))) - (completions (when input - (delete-region (point-marker) - (progn - (forward-char (- (length input))) - (point-marker))) - (process-send-string - process - (format - python-shell-completion-strings-code input)) - (accept-process-output process) - (save-excursion - (re-search-backward comint-prompt-regexp - comint-last-input-end t) + (with-syntax-table python-dotty-syntax-table + (when (and comint-last-prompt-overlay + (> (point-marker) (overlay-end comint-last-prompt-overlay))) + (let* ((process (get-buffer-process (current-buffer))) + (input (comint-word (current-word))) + (completions (when input + (delete-region (point-marker) + (progn + (forward-char (- (length input))) + (point-marker))) + (message (format python-shell-completion-strings-code input)) + (python-shell-send-string + (format python-shell-completion-strings-code input) + process) (split-string - (buffer-substring-no-properties - (point-marker) comint-last-input-end) - ";\\|\"\\|'\\|(" t)))) - (completion (when completions (try-completion input completions)))) - (when completions - (save-excursion - (forward-line -1) - (kill-line 1))) - (cond ((eq completion t) - (when input (insert input))) - ((null completion) - (when input (insert input)) - (message "Can't find completion for \"%s\"" input) - (ding)) - ((not (string= input completion)) - (insert completion)) - (t - (message "Making completion list...") - (when input (insert input)) - (with-output-to-temp-buffer "*Python Completions*" - (display-completion-list - (all-completions input completions)))))))) + (save-excursion + (if (not comint-last-output-start) + "" + (goto-char comint-last-output-start) + (buffer-substring-no-properties + (point-marker) (line-end-position)))) + ";\\|\"\\|'\\|(" t))) + (completion (when completions (try-completion input completions)))) + (when completions + (save-excursion + (forward-line -1) + (kill-line 1))) + (cond ((eq completion t) + (when input (insert input))) + ((null completion) + (when input (insert input)) + (message "Can't find completion for \"%s\"" input) + (ding)) + ((not (string= input completion)) + (insert completion)) + (t + (message "Making completion list...") + (when input (insert input)) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions))))))))) (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. -- cgit v1.2.1 From 075a0f6194c052a2dfc7bd7b8d267f5188fa1923 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:57 -0300 Subject: Enhanced shell and code autocompletion. python-shell-completion-complete-at-point and python-completion-complete-at-point now share common code. Also lots of fixes related to the cleanup of shell output has been made so completion code is really robust now. --- lisp/progmodes/python.el | 136 +++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 82 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b9cbfdd3b4d..114b2ff1775 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1138,6 +1138,45 @@ It is specially designed to be added to the (define-key inferior-python-mode-map (kbd "") 'python-shell-completion-complete-or-indent))) +(defun python-shell-completion--get-completions (input process) + "Retrieve available completions for INPUT using PROCESS." + (with-current-buffer (process-buffer process) + (let ((completions)) + (python-shell-send-string + (format python-shell-completion-strings-code input) + process) + (accept-process-output process) + (when comint-last-output-start + (setq completions + (split-string + (buffer-substring-no-properties + comint-last-output-start + (save-excursion + (goto-char comint-last-output-start) + (line-end-position))) + ";\\|\"\\|'\\|(" t)) + (comint-delete-output) + completions)))) + +(defun python-shell-completion--get-completion (input completions) + "Get completion for INPUT using COMPLETIONS." + (let ((completion (when completions + (try-completion input completions)))) + (cond ((eq completion t) + input) + ((null completion) + (message "Can't find completion for \"%s\"" input) + (ding) + input) + ((not (string= input completion)) + completion) + (t + (message "Making completion list...") + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions))) + input)))) + (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." (interactive) @@ -1145,43 +1184,12 @@ It is specially designed to be added to the (when (and comint-last-prompt-overlay (> (point-marker) (overlay-end comint-last-prompt-overlay))) (let* ((process (get-buffer-process (current-buffer))) - (input (comint-word (current-word))) - (completions (when input - (delete-region (point-marker) - (progn - (forward-char (- (length input))) - (point-marker))) - (message (format python-shell-completion-strings-code input)) - (python-shell-send-string - (format python-shell-completion-strings-code input) - process) - (split-string - (save-excursion - (if (not comint-last-output-start) - "" - (goto-char comint-last-output-start) - (buffer-substring-no-properties - (point-marker) (line-end-position)))) - ";\\|\"\\|'\\|(" t))) - (completion (when completions (try-completion input completions)))) - (when completions - (save-excursion - (forward-line -1) - (kill-line 1))) - (cond ((eq completion t) - (when input (insert input))) - ((null completion) - (when input (insert input)) - (message "Can't find completion for \"%s\"" input) - (ding)) - ((not (string= input completion)) - (insert completion)) - (t - (message "Making completion list...") - (when input (insert input)) - (with-output-to-temp-buffer "*Python Completions*" - (display-completion-list - (all-completions input completions))))))))) + (input (substring-no-properties + (or (comint-word (current-word)) "") nil nil))) + (delete-char (- (length input))) + (insert + (python-shell-completion--get-completion + input (python-shell-completion--get-completions input process))))))) (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. @@ -1292,51 +1300,15 @@ inferior python process is updated properly." (let ((process (python-shell-get-process))) (if (not process) (error "Completion needs an inferior Python process running") - (let* ((input (when (comint-word (current-word)) - (with-syntax-table python-dotty-syntax-table - (buffer-substring (point-marker) - (save-excursion - (forward-word -1) - (point-marker)))))) - (completions (when input - (delete-region (point-marker) - (progn - (forward-char (- (length input))) - (point-marker))) - (process-send-string - process - (format - python-shell-completion-strings-code input)) - (accept-process-output process) - (with-current-buffer (process-buffer process) - (save-excursion - (re-search-backward comint-prompt-regexp - comint-last-input-end t) - (split-string - (buffer-substring-no-properties - (point-marker) comint-last-input-end) - ";\\|\"\\|'\\|(" t))))) - (completion (when completions - (try-completion input completions)))) - (with-current-buffer (process-buffer process) - (save-excursion - (forward-line -1) - (kill-line 1))) - (when completions - (cond ((eq completion t) - (insert input)) - ((null completion) - (insert input) - (message "Can't find completion for \"%s\"" input) - (ding)) - ((not (string= input completion)) - (insert completion)) - (t - (message "Making completion list...") - (insert input) - (with-output-to-temp-buffer "*Python Completions*" - (display-completion-list - (all-completions input completions)))))))))) + (with-syntax-table python-dotty-syntax-table + (let* ((input (substring-no-properties + (or (comint-word (current-word)) "") nil nil)) + (completions (python-shell-completion--get-completions + input process))) + (delete-char (- (length input))) + (insert + (python-shell-completion--get-completion + input completions))))))) (add-to-list 'debug-ignored-errors "^Completion needs an inferior Python process running.") -- cgit v1.2.1 From db1497be1449738eeaa4f47d960d738316294331 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:57 -0300 Subject: Cleaned up TODO --- lisp/progmodes/python.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 114b2ff1775..5a3dc4bb1fb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -84,12 +84,13 @@ ;; Better decorator support for beginning of defun -;; Fix shell autocompletion when: obj. +;; Review code and cleanup -;; Remove garbage prompts left from calls to `comint-send-string' and -;; other comint related cleanups. +;; (Perhaps) python-check -;; Review code and cleanup +;; (Perhaps) ffap support + +;; (Perhaps) some skeletons (I never use them because of yasnippet) ;;; Code: -- cgit v1.2.1 From fc87f759ed9952e601e05cbd2229a1b3aec80158 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:57 -0300 Subject: Fixed called-interactively-p invocation. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5a3dc4bb1fb..78b541d8a0e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1023,7 +1023,7 @@ commands.)" "Send STRING to inferior Python process." (interactive "sPython command: ") (let ((process (or process (python-shell-get-or-create-process)))) - (when (called-interactively-p) + (when (called-interactively-p 'interactive) (message (format "Sent: %s..." string))) (comint-send-string process string) (when (or (not (string-match "\n$" string)) -- cgit v1.2.1 From ed0eb594649dbdfbf130c2b0d4e6297226258f87 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:58 -0300 Subject: Make shell use completion-at-point for autocompletion. --- lisp/progmodes/python.el | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 78b541d8a0e..af441460562 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -946,6 +946,10 @@ OUTPUT is a string with the contents of the buffer." 'python-pdbtrack-comint-output-filter-function) (set (make-local-variable 'compilation-error-regexp-alist) python-shell-compilation-regexp-alist) + (define-key inferior-python-mode-map [remap complete-symbol] + 'completion-at-point) + (add-hook 'completion-at-point-functions + 'python-shell-completion-complete-at-point nil 'local) (compilation-shell-minor-mode 1)) (defun run-python (dedicated cmd) -- cgit v1.2.1 From 9e6629387daaee10adb18baa892b4af58d2c5a33 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:58 -0300 Subject: Eldoc integration is now compatible with python 3 --- lisp/progmodes/python.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index af441460562..739b137d566 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1413,14 +1413,19 @@ Optional argument JUSTIFY defines if the paragraph should be justified." "def __PYDOC_get_help(obj): try: import pydoc - obj = eval(obj, globals()) - return pydoc.getdoc(obj) + if hasattr(obj, 'startswith'): + obj = eval(obj, globals()) + doc = pydoc.getdoc(obj) except: - return ''" + doc = '' + try: + exec('print doc') + except SyntaxError: + print(doc)" "Python code to setup documentation retrieval.") (defvar python-eldoc-string-code - "print __PYDOC_get_help('''%s''')\n" + "__PYDOC_get_help('''%s''')\n" "Python code used to get a string with the documentation of an object.") (defun python-eldoc-setup () -- cgit v1.2.1 From 138df813695f6434f986bd1c55dc3005ed32af75 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:59 -0300 Subject: Implemeneted python-shell-clear-latest-output and python-shell-send-and-clear-output Also Simplified python-shell-completion--get-completions using python-shell-send-and-clear-output --- lisp/progmodes/python.el | 58 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 17 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 739b137d566..ed6bb3189c9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1024,7 +1024,7 @@ commands.)" global-proc-buffer-name)))) (defun python-shell-send-string (string &optional process) - "Send STRING to inferior Python process." + "Send STRING to inferior Python PROCESS." (interactive "sPython command: ") (let ((process (or process (python-shell-get-or-create-process)))) (when (called-interactively-p 'interactive) @@ -1085,6 +1085,41 @@ When argument ARG is non-nil sends the innermost defun." full-file-name full-file-name) process))) +(defun python-shell-clear-latest-output () + "Clear latest output from the Python shell. +Return the cleaned output." + (interactive) + (when (and comint-last-output-start + comint-last-prompt-overlay) + (save-excursion + (let* ((last-output-end + (save-excursion + (goto-char + (overlay-start comint-last-prompt-overlay)) + (forward-comment -1) + (point-marker))) + (last-output + (buffer-substring-no-properties + comint-last-output-start last-output-end))) + (when (< 0 (length last-output)) + (goto-char comint-last-output-start) + (delete-region comint-last-output-start last-output-end) + (delete-char -1) + last-output))))) + +(defun python-shell-send-and-clear-output (string process) + "Send STRING to PROCESS and clear the output. +Return the cleaned output." + (interactive) + (python-shell-send-string string process) + (accept-process-output process) + (with-current-buffer (process-buffer process) + (let ((output (python-shell-clear-latest-output))) + (forward-line -1) + (kill-whole-line) + (goto-char (overlay-end comint-last-prompt-overlay)) + output))) + (defun python-shell-switch-to-shell () "Switch to inferior Python process buffer." (interactive) @@ -1146,22 +1181,11 @@ It is specially designed to be added to the (defun python-shell-completion--get-completions (input process) "Retrieve available completions for INPUT using PROCESS." (with-current-buffer (process-buffer process) - (let ((completions)) - (python-shell-send-string - (format python-shell-completion-strings-code input) - process) - (accept-process-output process) - (when comint-last-output-start - (setq completions - (split-string - (buffer-substring-no-properties - comint-last-output-start - (save-excursion - (goto-char comint-last-output-start) - (line-end-position))) - ";\\|\"\\|'\\|(" t)) - (comint-delete-output) - completions)))) + (split-string + (or (python-shell-send-and-clear-output + (format python-shell-completion-strings-code input) + process) "") + ";\\|\"\\|'\\|(" t))) (defun python-shell-completion--get-completion (input completions) "Get completion for INPUT using COMPLETIONS." -- cgit v1.2.1 From 1066882c6ddb30f786d665c5db305897f20f7119 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:59 -0300 Subject: Simplified python-eldoc-function using python-shell-send-and-clear-output --- lisp/progmodes/python.el | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ed6bb3189c9..735f25dfa54 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1494,18 +1494,8 @@ inferior python process is updated properly." (forward-char) (delete-region (point-marker) (search-forward "self.")) (setq input (buffer-substring (point-min) (point-max))))) - (process-send-string - process (format python-eldoc-string-code input)) - (accept-process-output process) - (with-current-buffer (process-buffer process) - (when comint-last-prompt-overlay - (save-excursion - (goto-char comint-last-input-end) - (re-search-forward comint-prompt-regexp - (line-end-position) t) - (buffer-substring-no-properties - (point-marker) - (overlay-start comint-last-prompt-overlay)))))))) + (python-shell-send-and-clear-output + (format python-eldoc-string-code input) process)))) (with-current-buffer (process-buffer process) (when comint-last-prompt-overlay (delete-region comint-last-input-end -- cgit v1.2.1 From 046428d32838bd413a44d221d929627949fb949a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:02:59 -0300 Subject: Added ffap support --- lisp/progmodes/python.el | 60 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 735f25dfa54..83d58360551 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -88,8 +88,6 @@ ;; (Perhaps) python-check -;; (Perhaps) ffap support - ;; (Perhaps) some skeletons (I never use them because of yasnippet) ;;; Code: @@ -1431,6 +1429,62 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (t t)))) +;;; FFAP + +(defvar python-ffap-setup-code + "def __FFAP_get_module_path(module): + try: + import os + path = __import__(module).__file__ + if path[-4:] == '.pyc' and os.path.exists(path[0:-1]): + path = path[:-1] + return path + except: + return ''" + "Python code to get a module path.") + +(defvar python-ffap-string-code + "__FFAP_get_module_path('''%s''')\n" + "Python code used to get a string with the path of a module.") + +(defun python-ffap-setup () + "Send `python-ffap-setup-code' to inferior Python process. +It is specially designed to be added to the +`inferior-python-mode-hook'." + (when python-ffap-setup-code + (let ((temp-file (make-temp-file "py"))) + (with-temp-file temp-file + (insert python-ffap-setup-code) + (delete-trailing-whitespace) + (goto-char (point-min))) + (python-shell-send-file temp-file (get-buffer-process (current-buffer))) + (message (format "FFAP setup code sent."))))) + +(defun python-ffap-module-path (module) + "Function for `ffap-alist' to return path for MODULE." + (let ((process (or + (and (eq major-mode 'inferior-python-mode) + (get-buffer-process (current-buffer))) + (python-shell-get-process)))) + (if (not process) + nil + (let ((module-file + (python-shell-send-and-clear-output + (format python-ffap-string-code module) process))) + (when module-file + (ffap-locate-file + (substring-no-properties module-file 1 -1) + nil nil)))))) + +(eval-after-load "ffap" + '(progn + (push '(python-mode . python-ffap-module-path) ffap-alist) + (push '(inferior-python-mode . python-ffap-module-path) ffap-alist))) + +(add-hook 'inferior-python-mode-hook + #'python-ffap-setup) + + ;;; Eldoc (defvar python-eldoc-setup-code @@ -1463,7 +1517,7 @@ It is specially designed to be added to the (delete-trailing-whitespace) (goto-char (point-min))) (python-shell-send-file temp-file (get-buffer-process (current-buffer))) - (message (format "Completion setup code sent."))))) + (message (format "Eldoc setup code sent."))))) (defun python-eldoc-function () "`eldoc-documentation-function' for Python. -- cgit v1.2.1 From 2947016aa73340d27552601deb112a78d61993d4 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:00 -0300 Subject: Small changes to ffap support --- lisp/progmodes/python.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 83d58360551..6284e64be8e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -66,6 +66,10 @@ ;; the shell completion in background so you should run ;; `python-shell-send-buffer' from time to time to get better results. +;; FFAP: You can find the filename for a given module when using ffap +;; out of the box. This feature needs an inferior python shell +;; running. + ;; Eldoc: returns documentation for object at point by using the ;; inferior python subprocess to inspect its documentation. As you ;; might guessed you should run `python-shell-send-buffer' from time @@ -1472,9 +1476,7 @@ It is specially designed to be added to the (python-shell-send-and-clear-output (format python-ffap-string-code module) process))) (when module-file - (ffap-locate-file - (substring-no-properties module-file 1 -1) - nil nil)))))) + (substring-no-properties module-file 1 -1)))))) (eval-after-load "ffap" '(progn -- cgit v1.2.1 From 8b3e0e76ee48f73c33d0d6af67fdc7be10497dae Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:00 -0300 Subject: Implemented python-check --- lisp/progmodes/python.el | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6284e64be8e..211978f15dd 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -70,6 +70,9 @@ ;; out of the box. This feature needs an inferior python shell ;; running. +;; Code check: Check the current file for errors using +;; `python-check-command' + ;; Eldoc: returns documentation for object at point by using the ;; inferior python subprocess to inspect its documentation. As you ;; might guessed you should run `python-shell-send-buffer' from time @@ -90,8 +93,6 @@ ;; Review code and cleanup -;; (Perhaps) python-check - ;; (Perhaps) some skeletons (I never use them because of yasnippet) ;;; Code: @@ -133,6 +134,8 @@ (define-key map "\C-c\C-c" 'python-shell-send-buffer) (define-key map "\C-c\C-l" 'python-shell-send-file) (define-key map "\C-c\C-z" 'python-shell-switch-to-shell) + ;; Some util commands + (define-key map "\C-c\C-v" 'python-check) ;; Utilities (substitute-key-definition 'complete-symbol 'completion-at-point map global-map) @@ -170,6 +173,8 @@ :help "Eval file in inferior Python session"] ["Debugger" pdb :help "Run pdb under GUD"] "-" + ["Check file" python-check + :help "Check file for errors"] ["Complete symbol" completion-at-point :help "Complete symbol before point"])) map) @@ -1487,6 +1492,34 @@ It is specially designed to be added to the #'python-ffap-setup) +;;; Code check + +(defvar python-check-command + "pychecker --stdlib" + "Command used to check a Python file.") + +(defvar python-check-custom-command nil + "Internal use.") + +(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 "Check command: " + (or python-check-custom-command + (concat python-check-command " " + (shell-quote-argument + (or + (let ((name (buffer-file-name))) + (and name + (file-name-nondirectory name))) + ""))))))) + (setq python-check-custom-command command) + (save-some-buffers (not compilation-ask-about-save) nil) + (compilation-start command)) + + ;;; Eldoc (defvar python-eldoc-setup-code -- cgit v1.2.1 From 78334b439dac55783347e4062cba58cfef638201 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:00 -0300 Subject: Implemented python-eldoc-at-point (python-describe-symbol replacement) --- lisp/progmodes/python.el | 53 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 211978f15dd..16a6d4b45d0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -136,6 +136,7 @@ (define-key map "\C-c\C-z" 'python-shell-switch-to-shell) ;; Some util commands (define-key map "\C-c\C-v" 'python-check) + (define-key map "\C-c\C-f" 'python-eldoc-at-point) ;; Utilities (substitute-key-definition 'complete-symbol 'completion-at-point map global-map) @@ -175,6 +176,8 @@ "-" ["Check file" python-check :help "Check file for errors"] + ["Help on symbol" python-eldoc-at-point + :help "Get help on symbol at point"] ["Complete symbol" completion-at-point :help "Complete symbol before point"])) map) @@ -1554,20 +1557,16 @@ It is specially designed to be added to the (python-shell-send-file temp-file (get-buffer-process (current-buffer))) (message (format "Eldoc setup code sent."))))) -(defun python-eldoc-function () - "`eldoc-documentation-function' for Python. -For this to work the best as possible you should call -`python-shell-send-buffer' from time to time so context in -inferior python process is updated properly." - (interactive) - (let ((process (python-shell-get-process))) +(defun python-eldoc--get-doc-at-point (&optional force-input force-process) + (let ((process (or force-process (python-shell-get-process)))) (if (not process) "Eldoc needs an inferior Python process running." (let* ((current-defun (python-info-current-defun)) - (input (with-syntax-table python-dotty-syntax-table - (if (not current-defun) - (current-word) - (concat current-defun "." (current-word))))) + (input (or force-input + (with-syntax-table python-dotty-syntax-table + (if (not current-defun) + (current-word) + (concat current-defun "." (current-word)))))) (ppss (syntax-ppss)) (help (when (and input (not (string= input (concat current-defun "."))) @@ -1593,6 +1592,38 @@ inferior python process is updated properly." (not (string= help "\n"))) help))))) +(defun python-eldoc-function () + "`eldoc-documentation-function' for Python. +For this to work the best as possible you should call +`python-shell-send-buffer' from time to time so context in +inferior python process is updated properly." + (python-eldoc--get-doc-at-point)) + +(defun python-eldoc-at-point (symbol) + "Get help on SYMBOL using `help'. +Interactively, prompt for symbol." + (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)))) + (let ((process (python-shell-get-process))) + (if (not process) + (message "Eldoc needs an inferior Python process running.") + (let ((temp-buffer-show-hook + (lambda () + (toggle-read-only 1) + (setq view-return-to-alist + (list (cons (selected-window) help-return-method)))))) + (with-output-to-temp-buffer (help-buffer) + (with-current-buffer standard-output + (insert + (python-eldoc--get-doc-at-point symbol process)) + (help-print-return-message))))))) + (add-hook 'inferior-python-mode-hook #'python-eldoc-setup) -- cgit v1.2.1 From 14d9f80c0d82a9c13f6e1e627eeb5ebac41a009a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:01 -0300 Subject: Fixed indentation guess logic to never accept 0 as a possible value --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 16a6d4b45d0..69df0535054 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -437,7 +437,9 @@ These make `python-indent-calculate-indentation' subtract the value of (forward-line 1)) (forward-line 1) (forward-comment 1) - (setq python-indent-offset (current-indentation))))))) + (let ((indent-offset (current-indentation))) + (when (> indent-offset 0) + (setq python-indent-offset indent-offset)))))))) (defun python-indent-context (&optional stop) "Return information on indentation context. -- cgit v1.2.1 From 534e24385bb1d5d022729e55d3d8cd2c6c114628 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:01 -0300 Subject: Fixed incorrect syntax highlighting for variable assignations --- lisp/progmodes/python.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 69df0535054..76901aaa69e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -278,7 +278,8 @@ (when (re-search-forward re limit t) (while (and (not (equal (nth 0 (syntax-ppss)) 0)) (re-search-forward re limit t))) - (if (equal (nth 0 (syntax-ppss)) 0) + (if (and (equal (nth 0 (syntax-ppss)) 0) + (not (equal (char-after (point-marker)) ?=))) t (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)) -- cgit v1.2.1 From 6eb68dc25b13771811fa7afc2d0b3b90347a57b9 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:01 -0300 Subject: Fixed pdb-track on Windows make-temp-file is returning the temp file path with the wrong type of slashes. --- lisp/progmodes/python.el | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 76901aaa69e..0611a4c9b45 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1061,6 +1061,17 @@ commands.)" (message (format "Sent: %s..." (buffer-substring (point-min) (line-end-position))))) + ;; Fix Windows/MS-DOS temp file path + (when (or (eq system-type 'windows-nt) + (eq system-type 'ms-dos) + (eq system-type 'cygwin)) + (setq temp-file + (with-temp-buffer + (insert temp-file) + (goto-char (point-min)) + (while (search-forward "/" nil t) + (replace-match "\\" nil t)) + (buffer-substring (point-min) (point-max))))) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file current-file) (setq inferior-python-mode-current-temp-file temp-file)) -- cgit v1.2.1 From d8e594dba8ac82b52f6921dab08b42de751634b7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:02 -0300 Subject: Fixed highlighting for dictionary assignments --- lisp/progmodes/python.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0611a4c9b45..24f232bb98c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -273,7 +273,8 @@ ;; asignations ;; support for a = b = c = 5 (,(lambda (limit) - (let ((re (python-rx (group (+ (any word ?. ?_))) (* space) + (let ((re (python-rx (group (+ (any word ?. ?_))) + (? ?\[ (+ (not (any ?\]))) ?\]) (* space) assignment-operator))) (when (re-search-forward re limit t) (while (and (not (equal (nth 0 (syntax-ppss)) 0)) -- cgit v1.2.1 From d818ffa8705888ee614de67e0e15f9576c3ef577 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:02 -0300 Subject: Use convert-standard-filename to fix temp files path --- lisp/progmodes/python.el | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 24f232bb98c..aef559f8b74 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1054,7 +1054,7 @@ commands.)" (let* ((contents (buffer-substring start end)) (current-file (buffer-file-name)) (process (python-shell-get-or-create-process)) - (temp-file (make-temp-file "py"))) + (temp-file (convert-standard-filename (make-temp-file "py")))) (with-temp-file temp-file (insert contents) (delete-trailing-whitespace) @@ -1062,17 +1062,6 @@ commands.)" (message (format "Sent: %s..." (buffer-substring (point-min) (line-end-position))))) - ;; Fix Windows/MS-DOS temp file path - (when (or (eq system-type 'windows-nt) - (eq system-type 'ms-dos) - (eq system-type 'cygwin)) - (setq temp-file - (with-temp-buffer - (insert temp-file) - (goto-char (point-min)) - (while (search-forward "/" nil t) - (replace-match "\\" nil t)) - (buffer-substring (point-min) (point-max))))) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file current-file) (setq inferior-python-mode-current-temp-file temp-file)) -- cgit v1.2.1 From 57808175eb11ac0cdcdeb9a545c7707da433c827 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:02 -0300 Subject: Added commentary about auto-indentation on newlines for python-mode.el users --- lisp/progmodes/python.el | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index aef559f8b74..a243f6cbe25 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -54,7 +54,7 @@ ;; word. Shell completion is implemented in a manner that if you ;; change the `python-shell-interpreter' to any other (for example ;; IPython) it should be easy to integrate another way to calculate -;; completions. You just need to especify your custom +;; completions. You just need to specify your custom ;; `python-shell-completion-setup-code' and ;; `python-shell-completion-strings-code' @@ -78,6 +78,21 @@ ;; might guessed you should run `python-shell-send-buffer' from time ;; to time to get better results too. +;; If you used python-mode.el you probably will miss auto-indentation +;; when inserting newlines. To achieve the same behavior you have +;; two options: + +;; 1) Use GNU/Emacs' standard binding for `newline-and-indent': C-j. + +;; 2) Add the following hook in your .emacs: + +;; (add-hook 'python-mode-hook +;; #'(lambda () +;; (define-key python-mode-map "\C-m" 'newline-and-indent))) + +;; I'd recommend the first one since you'll get the same behavior for +;; all modes out-of-the-box. + ;;; Installation: ;; Add this to your .emacs: -- cgit v1.2.1 From d439cda5982b3c56b6817870f075bcc8860f7785 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:03 -0300 Subject: Enhanced python-shell-send-file function python-shell-send-file function now can be called interactively and will do the right thing. Also the python code that sent the file was improved so the shell considers the correct path when evaluating the file. Removed the inferior-python-mode-current-temp-file variable, after this update inferior-python-mode-current-file is enough. --- lisp/progmodes/python.el | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a243f6cbe25..317ef2e1282 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -956,10 +956,6 @@ OUTPUT is a string with the contents of the buffer." "Current file from which a region was sent.") (make-variable-buffer-local 'inferior-python-mode-current-file) -(defvar inferior-python-mode-current-temp-file nil - "Current temp file sent to process.") -(make-variable-buffer-local 'inferior-python-mode-current-file) - (define-derived-mode inferior-python-mode comint-mode "Inferior Python" "Major mode for Python inferior process." (set-syntax-table python-mode-syntax-table) @@ -1077,10 +1073,7 @@ commands.)" (message (format "Sent: %s..." (buffer-substring (point-min) (line-end-position))))) - (with-current-buffer (process-buffer process) - (setq inferior-python-mode-current-file current-file) - (setq inferior-python-mode-current-temp-file temp-file)) - (python-shell-send-file temp-file process))) + (python-shell-send-file current-file process temp-file))) (defun python-shell-send-buffer () "Send the entire buffer to inferior Python process." @@ -1103,15 +1096,26 @@ When argument ARG is non-nil sends the innermost defun." (or (python-end-of-defun-function) (progn (end-of-line) (point-marker))))))) -(defun python-shell-send-file (file-name &optional process) - "Send FILE-NAME to inferior Python PROCESS." +(defun python-shell-send-file (file-name &optional process temp-file-name) + "Send FILE-NAME to inferior Python PROCESS. +If TEMP-FILE-NAME is passed then that file is used for processing +instead, while internally the shell will continue to use +FILE-NAME." (interactive "fFile to send: ") (let ((process (or process (python-shell-get-or-create-process))) - (full-file-name (expand-file-name file-name))) + (file-name (convert-standard-filename (expand-file-name file-name))) + (temp-file-name (when temp-file-name + (convert-standard-filename + (expand-file-name temp-file-name))))) + (find-file-noselect file-name) + (with-current-buffer (process-buffer process) + (setq inferior-python-mode-current-file file-name)) (python-shell-send-string (format - "__pyfile = open('%s'); exec(compile(__pyfile.read(), '%s', 'exec')); __pyfile.close()" - full-file-name full-file-name) + (concat "__pyfile = open('''%s''');" + "exec(compile(__pyfile.read(), '''%s''', 'exec'));" + "__pyfile.close()") + (or temp-file-name file-name) file-name) process))) (defun python-shell-clear-latest-output () @@ -1289,8 +1293,7 @@ Returns a cons with the form: (or (assq (current-buffer) python-pdbtrack-tracking-buffers) (let* ((file (with-current-buffer (current-buffer) - (or inferior-python-mode-current-file - inferior-python-mode-current-temp-file))) + inferior-python-mode-current-file)) (tracking-buffers `(,(current-buffer) . ,(or (get-file-buffer file) @@ -1316,7 +1319,7 @@ Argument OUTPUT is a string with the output from the comint process." (string-match (format python-pdbtrack-stacktrace-info-regexp (regexp-quote - inferior-python-mode-current-temp-file)) + inferior-python-mode-current-file)) full-output) (string-to-number (or (match-string-no-properties 1 full-output) "")))) (tracked-buffer-window (get-buffer-window (cdr tracking-buffers))) @@ -1577,6 +1580,10 @@ It is specially designed to be added to the (message (format "Eldoc setup code sent."))))) (defun python-eldoc--get-doc-at-point (&optional force-input force-process) + "Internal implementation to get documentation at point. +If not FORCE-INPUT is passed then what `current-word' returns +will be used. If not FORCE-PROCESS is passed what +`python-shell-get-process' returns is used." (let ((process (or force-process (python-shell-get-process)))) (if (not process) "Eldoc needs an inferior Python process running." -- cgit v1.2.1 From 24b68537c35c7c7a11be02242a6f3813f279902c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:03 -0300 Subject: Make inferior-python-mode-current-file be set via convert-standard-filename --- lisp/progmodes/python.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 317ef2e1282..1fd4f326d3c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1065,7 +1065,7 @@ commands.)" (let* ((contents (buffer-substring start end)) (current-file (buffer-file-name)) (process (python-shell-get-or-create-process)) - (temp-file (convert-standard-filename (make-temp-file "py")))) + (temp-file (make-temp-file "py"))) (with-temp-file temp-file (insert contents) (delete-trailing-whitespace) @@ -1103,13 +1103,13 @@ instead, while internally the shell will continue to use FILE-NAME." (interactive "fFile to send: ") (let ((process (or process (python-shell-get-or-create-process))) - (file-name (convert-standard-filename (expand-file-name file-name))) + (file-name (expand-file-name file-name)) (temp-file-name (when temp-file-name - (convert-standard-filename - (expand-file-name temp-file-name))))) + (expand-file-name temp-file-name)))) (find-file-noselect file-name) (with-current-buffer (process-buffer process) - (setq inferior-python-mode-current-file file-name)) + (setq inferior-python-mode-current-file + (convert-standard-filename file-name))) (python-shell-send-string (format (concat "__pyfile = open('''%s''');" -- cgit v1.2.1 From 17d13b85268a6b864e92af05cb8f0ed4461e3cc5 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:03 -0300 Subject: Fixed indentation of multi-line function call's closing parenthesis --- lisp/progmodes/python.el | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1fd4f326d3c..564638c1b58 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -599,24 +599,32 @@ START is the buffer position where the sexp starts." (current-indentation))))) indentation)) ('inside-paren - (- - (save-excursion - (goto-char context-start) - (forward-char) - (save-restriction - (narrow-to-region - (line-beginning-position) - (line-end-position)) - (forward-comment 1)) - (if (looking-at "$") - (+ (current-indentation) python-indent-offset) - (forward-comment 1) - (current-column))) - (if (progn - (back-to-indentation) - (looking-at (regexp-opt '(")" "]" "}")))) - python-indent-offset - 0)))))))) + (or (save-excursion + (forward-comment 1) + (looking-at (regexp-opt '(")" "]" "}"))) + (forward-char 1) + (when (not (nth 1 (syntax-ppss))) + (goto-char context-start) + (back-to-indentation) + (current-column))) + (- + (save-excursion + (goto-char context-start) + (forward-char) + (save-restriction + (narrow-to-region + (line-beginning-position) + (line-end-position)) + (forward-comment 1)) + (if (looking-at "$") + (+ (current-indentation) python-indent-offset) + (forward-comment 1) + (current-column))) + (if (progn + (back-to-indentation) + (looking-at (regexp-opt '(")" "]" "}")))) + python-indent-offset + 0))))))))) (defun python-indent-calculate-levels () "Calculate `python-indent-levels' and reset `python-indent-current-level'." -- cgit v1.2.1 From ffdb56c385f40e22031828cf08b7dd8482aea109 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:04 -0300 Subject: Implemented python-indent-electric-colon --- lisp/progmodes/python.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 564638c1b58..76546aa9799 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -142,6 +142,7 @@ (define-key map (kbd "") 'python-indent-dedent-line) (define-key map "\C-c<" 'python-indent-shift-left) (define-key map "\C-c>" 'python-indent-shift-right) + (define-key map ":" 'python-indent-electric-colon) ;; Shell interaction (define-key map "\C-c\C-s" 'python-shell-send-string) (define-key map "\C-c\C-r" 'python-shell-send-region) @@ -786,6 +787,19 @@ lie." (setq count python-indent-offset)) (indent-rigidly start end count)) +;; Directly from Dave Love's python.el +(defun python-indent-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) + (not (nth 8 (syntax-ppss))) + (save-excursion (python-indent-line)))) +(put 'python-indent-electric-colon 'delete-selection t) + ;;; Navigation -- cgit v1.2.1 From 76eefb1fe574c776c21282691aacc6fea3475abe Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:04 -0300 Subject: Small fix to python-indent-electric-colon Check current indentation is greater than the current calculated indentation. --- lisp/progmodes/python.el | 1 + 1 file changed, 1 insertion(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 76546aa9799..3863a9f851e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -797,6 +797,7 @@ just insert a single colon." (and (not arg) (eolp) (not (nth 8 (syntax-ppss))) + (> (current-indentation) (python-indent-calculate-indentation)) (save-excursion (python-indent-line)))) (put 'python-indent-electric-colon 'delete-selection t) -- cgit v1.2.1 From cb42456fc6d08f5e9db5f8c33d1647b248eb79c5 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:04 -0300 Subject: Don't deactivate mark after indenting commands --- lisp/progmodes/python.el | 63 +++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 30 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3863a9f851e..7da5599d6f0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -720,24 +720,25 @@ point is not in between the indentation." "Indent a python region automagically. Called from a program, START and END specify the region to indent." - (save-excursion - (goto-char end) - (setq end (point-marker)) - (goto-char start) - (or (bolp) (forward-line 1)) - (while (< (point) end) - (or (and (bolp) (eolp)) - (let (word) - (forward-line -1) - (back-to-indentation) - (setq word (current-word)) - (forward-line 1) - (when word - (beginning-of-line) - (delete-horizontal-space) - (indent-to (python-indent-calculate-indentation))))) - (forward-line 1)) - (move-marker end nil))) + (let ((deactivate-mark nil)) + (save-excursion + (goto-char end) + (setq end (point-marker)) + (goto-char start) + (or (bolp) (forward-line 1)) + (while (< (point) end) + (or (and (bolp) (eolp)) + (let (word) + (forward-line -1) + (back-to-indentation) + (setq word (current-word)) + (forward-line 1) + (when word + (beginning-of-line) + (delete-horizontal-space) + (indent-to (python-indent-calculate-indentation))))) + (forward-line 1)) + (move-marker end nil)))) (defun python-indent-shift-left (start end &optional count) "Shift lines contained in region START END by COUNT columns to the left. @@ -758,14 +759,15 @@ than COUNT columns." (setq count (prefix-numeric-value count)) (setq count python-indent-offset)) (when (> count 0) - (save-excursion - (goto-char start) - (while (< (point) end) - (if (and (< (current-indentation) count) - (not (looking-at "[ \t]*$"))) - (error "Can't shift all lines enough")) - (forward-line)) - (indent-rigidly start end (- count))))) + (let ((deactivate-mark nil)) + (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") @@ -782,10 +784,11 @@ lie." (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-offset)) - (indent-rigidly start end count)) + (let ((deactivate-mark nil)) + (if count + (setq count (prefix-numeric-value count)) + (setq count python-indent-offset)) + (indent-rigidly start end count))) ;; Directly from Dave Love's python.el (defun python-indent-electric-colon (arg) -- cgit v1.2.1 From 67845102b2dc195067a6048631a8319d735497a3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:05 -0300 Subject: Explain we have python-indent-electric-colon --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 7da5599d6f0..49b351cc17c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -41,7 +41,9 @@ ;; Indentation: Automatic indentation with indentation cycling is ;; provided, it allows you to navigate different available levels of -;; indentation by hitting several times. +;; indentation by hitting several times. Also when inserting a +;; colon the `python-indent-electric-colon' command is invoked and +;; causes the current line to be dedented automatically if needed. ;; Movement: `beginning-of-defun' and `end-of-defun' functions are ;; properly implemented. A `beginning-of-innermost-defun' is defined -- cgit v1.2.1 From e2803784cfbd622a60874f3a1d20a0e0decd7161 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:05 -0300 Subject: Implemented Skeletons after GNU/Emacs python.el 6 basic skeletons are defined: class, def, for, if, try and while. While these skeletons are strongly based on GNU/Emacs' current python.el a better definition macro, a generic template for try/except/finally/else blocks and a cool menu display is included. --- lisp/progmodes/python.el | 158 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 49b351cc17c..fa00faf1502 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -68,6 +68,13 @@ ;; the shell completion in background so you should run ;; `python-shell-send-buffer' from time to time to get better results. +;; Skeletons: 6 skeletons are provided for simple inserting of class, +;; def, for, if, try and while. These skeletons are integrated with +;; dabbrev. If you have `dabbrev-mode' activated and +;; `python-skeleton-autoinsert' is set to t, then whenever you type +;; the name of any of those defined and hit SPC, they will be +;; automatically expanded. + ;; FFAP: You can find the filename for a given module when using ffap ;; out of the box. This feature needs an inferior python shell ;; running. @@ -110,8 +117,6 @@ ;; Review code and cleanup -;; (Perhaps) some skeletons (I never use them because of yasnippet) - ;;; Code: (require 'comint) @@ -145,6 +150,13 @@ (define-key map "\C-c<" 'python-indent-shift-left) (define-key map "\C-c>" 'python-indent-shift-right) (define-key map ":" 'python-indent-electric-colon) + ;; Skeletons + (define-key map "\C-c\C-tc" 'python-skeleton-class) + (define-key map "\C-c\C-td" 'python-skeleton-def) + (define-key map "\C-c\C-tf" 'python-skeleton-for) + (define-key map "\C-c\C-ti" 'python-skeleton-if) + (define-key map "\C-c\C-tt" 'python-skeleton-try) + (define-key map "\C-c\C-tw" 'python-skeleton-while) ;; Shell interaction (define-key map "\C-c\C-s" 'python-shell-send-string) (define-key map "\C-c\C-r" 'python-shell-send-region) @@ -176,6 +188,8 @@ ["End of def/class" end-of-defun :help "Go to end of definition around point"] "-" + ("Skeletons") + "-" ["Start interpreter" run-python :help "Run inferior Python process in a separate buffer"] ["Switch to shell" python-shell-switch-to-shell @@ -1491,6 +1505,138 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (t t)))) +;;; Skeletons + +(defcustom python-skeleton-autoinsert 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) + +(defvar python-skeleton-available '() + "Internal list of available skeletons.") +(make-variable-buffer-local 'inferior-python-mode-current-file) + +(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 () + (message "ppss %s" (not (nth 8 (syntax-ppss)))) + (message "autoinsert %s" python-skeleton-autoinsert) + (and + (not (nth 8 (syntax-ppss))) + python-skeleton-autoinsert))) + +(defmacro python-skeleton-define (name doc &rest skel) + "Define a `python-mode' skeleton using NAME DOC and SKEL. +The skeleton will be bound to python-skeleton-NAME and will +be added to `python-mode-abbrev-table'." + (let* ((name (symbol-name name)) + (function-name (intern (concat "python-skeleton-" name)))) + (define-abbrev python-mode-abbrev-table name "" function-name) + (setq python-skeleton-available + (cons function-name python-skeleton-available)) + `(define-skeleton ,function-name + ,(or doc + (format "Insert %s statement." name)) + ,@skel))) + +(put 'python-skeleton-define 'lisp-indent-function 2) + +(defmacro python-define-auxiliary-skeleton (name doc &optional &rest skel) + "Define a `python-mode' auxiliary skeleton using NAME DOC and SKEL. +The skeleton will be bound to python-skeleton-NAME." + (let* ((name (symbol-name name)) + (function-name (intern (concat "python-skeleton--" name))) + (msg (format + "Add '%s' clause? " name))) + (when (not skel) + (setq skel + `(< ,(format "%s:" name) \n \n + > _ \n))) + `(define-skeleton ,function-name + ,(or doc + (format "Auxiliary skeleton for %s statement." name)) + nil + (unless (y-or-n-p ,msg) + (signal 'quit t)) + ,@skel))) +(put 'python-define-auxiliary-skeleton 'lisp-indent-function 2) + +(python-define-auxiliary-skeleton else nil) + +(python-define-auxiliary-skeleton except nil) + +(python-define-auxiliary-skeleton finally nil) + +(python-skeleton-define if nil + "Condition: " + "if " str ":" \n + _ \n + ("other condition, %s: " + < + "elif " str ":" \n + > _ \n nil) + '(python-skeleton--else) | ^) + +(python-skeleton-define while nil + "Condition: " + "while " str ":" \n + > _ \n + '(python-skeleton--else) | ^) + +(python-skeleton-define for nil + "Iteration spec: " + "for " str ":" \n + > _ \n + '(python-skeleton--else) | ^) + +(python-skeleton-define try nil + nil + "try:" \n + > _ \n + ("Exception, %s: " + < + "except " str ":" \n + > _ \n nil) + resume: + '(python-skeleton--except) + '(python-skeleton--else) + '(python-skeleton--finally) | ^) + +(python-skeleton-define def nil + "Function name: " + "def " str " (" ("Parameter, %s: " + (unless (equal ?\( (char-before)) ", ") + str) "):" \n + "\"\"\"" - "\"\"\"" \n + > _ \n) + +(python-skeleton-define class nil + "Class name: " + "class " str " (" ("Inheritance, %s: " + (unless (equal ?\( (char-before)) ", ") + str) + & ")" | -2 + ":" \n + "\"\"\"" - "\"\"\"" \n + > _ \n) + +(defun python-skeleton-add-menu-items () + "Add menu items to Python->Skeletons menu." + (let ((skeletons (sort python-skeleton-available 'string<)) + (items)) + (dolist (skeleton skeletons) + (easy-menu-add-item + nil '("Python" "Skeletons") + `[,(format + "Insert %s" (caddr (split-string (symbol-name skeleton) "-"))) + ,skeleton t])))) + ;;; FFAP (defvar python-ffap-setup-code @@ -1822,6 +1968,12 @@ not inside a defun." (set (make-local-variable 'add-log-current-defun-function) #'python-info-current-defun) + (set (make-local-variable 'skeleton-further-elements) + '((abbrev-mode nil) + (< '(backward-delete-char-untabify (min python-indent-offset + (current-column)))) + (^ '(- (1+ (current-indentation)))))) + (set (make-local-variable 'eldoc-documentation-function) #'python-eldoc-function) @@ -1838,6 +1990,8 @@ not inside a defun." "`outline-level' function for Python mode." (1+ (/ (current-indentation) python-indent-offset)))) + (python-skeleton-add-menu-items) + (when python-indent-guess-indent-offset (python-indent-guess-indent-offset))) -- cgit v1.2.1 From 73ed683681d2941a1ae6f781f6c3c3f339d42fbd Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:05 -0300 Subject: python.el now bytecompiles without warnings --- lisp/progmodes/python.el | 58 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index fa00faf1502..c40c2e77d0d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -119,12 +119,15 @@ ;;; Code: -(require 'comint) (require 'ansi-color) -(require 'outline) +(require 'comint) (eval-when-compile - (require 'cl)) + (require 'cl) + ;; Avoid compiler warnings + (defvar view-return-to-alist) + (defvar compilation-error-regexp-alist) + (defvar outline-heading-end-regexp)) (autoload 'comint-mode "comint") @@ -218,22 +221,23 @@ ;;; Python specialized rx -(defconst python-rx-constituents - (list - `(block-start . ,(rx symbol-start - (or "def" "class" "if" "elif" "else" "try" - "except" "finally" "for" "while" "with") - symbol-end)) - `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) - `(open-paren . ,(rx (or "{" "[" "("))) - `(close-paren . ,(rx (or "}" "]" ")"))) - `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) - `(not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) - `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" - "=" "%" "**" "//" "<<" ">>" "<=" "!=" - "==" ">=" "is" "not"))) - `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" - ">>=" "<<=" "&=" "^=" "|="))))) +(eval-when-compile + (defconst python-rx-constituents + (list + `(block-start . ,(rx symbol-start + (or "def" "class" "if" "elif" "else" "try" + "except" "finally" "for" "while" "with") + symbol-end)) + `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) + `(open-paren . ,(rx (or "{" "[" "("))) + `(close-paren . ,(rx (or "}" "]" ")"))) + `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) + `(not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) + `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" + "=" "%" "**" "//" "<<" ">>" "<=" "!=" + "==" ">=" "is" "not"))) + `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" + ">>=" "<<=" "&=" "^=" "|=")))))) (defmacro python-rx (&rest regexps) "Python mode especialized rx macro which supports common python named REGEXPS." @@ -1537,14 +1541,14 @@ The skeleton will be bound to python-skeleton-NAME and will be added to `python-mode-abbrev-table'." (let* ((name (symbol-name name)) (function-name (intern (concat "python-skeleton-" name)))) - (define-abbrev python-mode-abbrev-table name "" function-name) - (setq python-skeleton-available - (cons function-name python-skeleton-available)) - `(define-skeleton ,function-name - ,(or doc - (format "Insert %s statement." name)) - ,@skel))) - + `(progn + (define-abbrev python-mode-abbrev-table ,name "" ',function-name) + (setq python-skeleton-available + (cons ',function-name python-skeleton-available)) + (define-skeleton ,function-name + ,(or doc + (format "Insert %s statement." name)) + ,@skel)))) (put 'python-skeleton-define 'lisp-indent-function 2) (defmacro python-define-auxiliary-skeleton (name doc &optional &rest skel) -- cgit v1.2.1 From 9ce938be0bd1381971dca8fa5c87d4f7dd449501 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:06 -0300 Subject: Shell integration improvements and cleanups Removed functions python-shell-clear-latest-output and python-shell-send-and-clear-output in favor of python-shell-send-string-no-output. Also python-shell-send-string now supports multiline string statements so you won't have to worry calling python-shell-send-file again. All this changes should make integrations with other Python shells than standard more robust. --- lisp/progmodes/python.el | 144 +++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 85 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c40c2e77d0d..6c4ecd97eab 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1094,32 +1094,49 @@ commands.)" dedicated-proc-buffer-name global-proc-buffer-name)))) -(defun python-shell-send-string (string &optional process) - "Send STRING to inferior Python PROCESS." +(defun python-shell-send-string (string &optional process msg) + "Send STRING to inferior Python PROCESS. +When MSG is non-nil messages the first line of STRING." (interactive "sPython command: ") - (let ((process (or process (python-shell-get-or-create-process)))) - (when (called-interactively-p 'interactive) - (message (format "Sent: %s..." string))) - (comint-send-string process string) - (when (or (not (string-match "\n$" string)) - (string-match "\n[ \t].*\n?$" string)) - (comint-send-string process "\n")))) + (let ((process (or process (python-shell-get-or-create-process))) + (lines (split-string string "\n" t))) + (when msg + (message (format "Sent: %s..." (nth 0 lines)))) + (if (> (length lines) 1) + (let* ((temp-file-name (make-temp-file "py")) + (file-name (or (buffer-file-name) temp-file-name))) + (with-temp-file temp-file-name + (insert string) + (delete-trailing-whitespace)) + (python-shell-send-file file-name process temp-file-name)) + (comint-send-string process string) + (when (or (not (string-match "\n$" string)) + (string-match "\n[ \t].*\n?$" string)) + (comint-send-string process "\n"))))) + +(defun python-shell-send-string-no-output (string &optional process msg) + "Send STRING to PROCESS and inhibit output. +When MSG is non-nil messages the first line of STRING. +Return the output." + (let* ((output-buffer) + (process (or process (python-shell-get-or-create-process))) + (comint-preoutput-filter-functions + (append comint-preoutput-filter-functions + '(ansi-color-filter-apply + (lambda (string) + (setq output-buffer (concat output-buffer string)) + ""))))) + (python-shell-send-string string process msg) + (accept-process-output process) + (mapconcat + (lambda (string) string) + (butlast (split-string output-buffer "\n")) "\n"))) (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." (interactive "r") - (let* ((contents (buffer-substring start end)) - (current-file (buffer-file-name)) - (process (python-shell-get-or-create-process)) - (temp-file (make-temp-file "py"))) - (with-temp-file temp-file - (insert contents) - (delete-trailing-whitespace) - (goto-char (point-min)) - (message (format "Sent: %s..." - (buffer-substring (point-min) - (line-end-position))))) - (python-shell-send-file current-file process temp-file))) + (let ((deactivate-mark nil)) + (python-shell-send-string (buffer-substring start end) nil t))) (defun python-shell-send-buffer () "Send the entire buffer to inferior Python process." @@ -1148,10 +1165,12 @@ If TEMP-FILE-NAME is passed then that file is used for processing instead, while internally the shell will continue to use FILE-NAME." (interactive "fFile to send: ") - (let ((process (or process (python-shell-get-or-create-process))) - (file-name (expand-file-name file-name)) - (temp-file-name (when temp-file-name - (expand-file-name temp-file-name)))) + (let* ((process (or process (python-shell-get-or-create-process))) + (temp-file-name (when temp-file-name + (expand-file-name temp-file-name))) + (file-name (or (expand-file-name file-name) temp-file-name))) + (when (not file-name) + (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) (find-file-noselect file-name) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file @@ -1164,41 +1183,6 @@ FILE-NAME." (or temp-file-name file-name) file-name) process))) -(defun python-shell-clear-latest-output () - "Clear latest output from the Python shell. -Return the cleaned output." - (interactive) - (when (and comint-last-output-start - comint-last-prompt-overlay) - (save-excursion - (let* ((last-output-end - (save-excursion - (goto-char - (overlay-start comint-last-prompt-overlay)) - (forward-comment -1) - (point-marker))) - (last-output - (buffer-substring-no-properties - comint-last-output-start last-output-end))) - (when (< 0 (length last-output)) - (goto-char comint-last-output-start) - (delete-region comint-last-output-start last-output-end) - (delete-char -1) - last-output))))) - -(defun python-shell-send-and-clear-output (string process) - "Send STRING to PROCESS and clear the output. -Return the cleaned output." - (interactive) - (python-shell-send-string string process) - (accept-process-output process) - (with-current-buffer (process-buffer process) - (let ((output (python-shell-clear-latest-output))) - (forward-line -1) - (kill-whole-line) - (goto-char (overlay-end comint-last-prompt-overlay)) - output))) - (defun python-shell-switch-to-shell () "Switch to inferior Python process buffer." (interactive) @@ -1243,14 +1227,10 @@ Also binds to `python-shell-complete-or-indent' in the It is specially designed to be added to the `inferior-python-mode-hook'." (when python-shell-completion-setup-code - (let ((temp-file (make-temp-file "py")) - (process (get-buffer-process (current-buffer)))) - (with-temp-file temp-file - (insert python-shell-completion-setup-code) - (delete-trailing-whitespace) - (goto-char (point-min))) - (python-shell-send-file temp-file process) - (message (format "Completion setup code sent."))) + (python-shell-send-string-no-output + python-shell-completion-setup-code + (get-buffer-process (current-buffer))) + (message "Completion setup code sent.") (add-to-list (make-local-variable 'comint-dynamic-complete-functions) 'python-shell-completion-complete-at-point) @@ -1261,7 +1241,7 @@ It is specially designed to be added to the "Retrieve available completions for INPUT using PROCESS." (with-current-buffer (process-buffer process) (split-string - (or (python-shell-send-and-clear-output + (or (python-shell-send-string-no-output (format python-shell-completion-strings-code input) process) "") ";\\|\"\\|'\\|(" t))) @@ -1664,13 +1644,10 @@ The skeleton will be bound to python-skeleton-NAME." It is specially designed to be added to the `inferior-python-mode-hook'." (when python-ffap-setup-code - (let ((temp-file (make-temp-file "py"))) - (with-temp-file temp-file - (insert python-ffap-setup-code) - (delete-trailing-whitespace) - (goto-char (point-min))) - (python-shell-send-file temp-file (get-buffer-process (current-buffer))) - (message (format "FFAP setup code sent."))))) + (python-shell-send-string-no-output + python-ffap-setup-code + (get-buffer-process (current-buffer))) + (message "FFAP setup code sent."))) (defun python-ffap-module-path (module) "Function for `ffap-alist' to return path for MODULE." @@ -1681,7 +1658,7 @@ It is specially designed to be added to the (if (not process) nil (let ((module-file - (python-shell-send-and-clear-output + (python-shell-send-string-no-output (format python-ffap-string-code module) process))) (when module-file (substring-no-properties module-file 1 -1)))))) @@ -1749,13 +1726,10 @@ Runs COMMAND, a shell command, as if by `compile'. See It is specially designed to be added to the `inferior-python-mode-hook'." (when python-eldoc-setup-code - (let ((temp-file (make-temp-file "py"))) - (with-temp-file temp-file - (insert python-eldoc-setup-code) - (delete-trailing-whitespace) - (goto-char (point-min))) - (python-shell-send-file temp-file (get-buffer-process (current-buffer))) - (message (format "Eldoc setup code sent."))))) + (python-shell-send-string-no-output + python-eldoc-setup-code + (get-buffer-process (current-buffer))) + (message "Eldoc setup code sent."))) (defun python-eldoc--get-doc-at-point (&optional force-input force-process) "Internal implementation to get documentation at point. @@ -1786,7 +1760,7 @@ will be used. If not FORCE-PROCESS is passed what (forward-char) (delete-region (point-marker) (search-forward "self.")) (setq input (buffer-substring (point-min) (point-max))))) - (python-shell-send-and-clear-output + (python-shell-send-string-no-output (format python-eldoc-string-code input) process)))) (with-current-buffer (process-buffer process) (when comint-last-prompt-overlay -- cgit v1.2.1 From 62feb9156582ff25dd6ce05499506248e2b1521b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:06 -0300 Subject: Better non-standard shell integration support Added python-shell-prompt-output-regexp to match the prompts added before output in shells like iPython. With the value of this variable the output generated for python-shell-send-string-no-ouput is cleaned up. Moved completion variables and bindings setup for shell to inferior-python-mode definition. Renamed python-shell-completion-strings-code to python-shell-completion-string-code. improved python-shell-completion--get-completions string splitting. Cleaned up some unecessary messages. Better code sending need test for python-shell-completion-setup python-ffap-setup and python-eldoc-setup. Added example for iPython integration in the commentary section. --- lisp/progmodes/python.el | 78 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 23 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6c4ecd97eab..6a5435afb24 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -58,7 +58,19 @@ ;; IPython) it should be easy to integrate another way to calculate ;; completions. You just need to specify your custom ;; `python-shell-completion-setup-code' and -;; `python-shell-completion-strings-code' +;; `python-shell-completion-string-code' + +;; Here is a complete example of the settings you would use for +;; iPython + +;; (setq +;; python-shell-interpreter "ipython" +;; python-shell-interpreter-args "" +;; python-shell-prompt-regexp "In \\[[0-9]+\\]: " +;; python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " +;; python-shell-completion-setup-code "" +;; python-shell-completion-string-code +;; "';'.join(__IP.complete('''%s'''))\n") ;; Pdb tracking: when you execute a block of code that contains some ;; call to pdb (or ipdb) it will prompt the block of code and will @@ -948,6 +960,13 @@ The regex should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) +(defcustom python-shell-prompt-output-regexp nil + "Regex matching output prompt of python shell. +The regex should not contain a caret (^) at the beginning." + :type 'string + :group 'python + :safe 'stringp) + (defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " "Regex matching pdb input prompt of python shell. The regex should not contain a caret (^) at the beginning." @@ -1003,7 +1022,11 @@ OUTPUT is a string with the contents of the buffer." (make-variable-buffer-local 'inferior-python-mode-current-file) (define-derived-mode inferior-python-mode comint-mode "Inferior Python" - "Major mode for Python inferior process." + "Major mode for Python inferior process. +Adds `python-shell-completion-complete-at-point' to the +`comint-dynamic-complete-functions' list. Also binds to +`python-shell-complete-or-indent' in the +`inferior-python-mode-map'." (set-syntax-table python-mode-syntax-table) (setq mode-line-process '(":%s")) (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" @@ -1021,6 +1044,10 @@ OUTPUT is a string with the contents of the buffer." 'completion-at-point) (add-hook 'completion-at-point-functions 'python-shell-completion-complete-at-point nil 'local) + (add-to-list (make-local-variable 'comint-dynamic-complete-functions) + 'python-shell-completion-complete-at-point) + (define-key inferior-python-mode-map (kbd "") + 'python-shell-completion-complete-or-indent) (compilation-shell-minor-mode 1)) (defun run-python (dedicated cmd) @@ -1128,6 +1155,21 @@ Return the output." ""))))) (python-shell-send-string string process msg) (accept-process-output process) + ;; Cleanup output prompt regexp + (when (and (not (string= "" output-buffer)) + (> (length python-shell-prompt-output-regexp) 0)) + (setq output-buffer + (with-temp-buffer + (insert-string output-buffer) + (goto-char (point-min)) + (forward-comment 1) + (buffer-substring-no-properties + (or + (and (looking-at python-shell-prompt-output-regexp) + (re-search-forward + python-shell-prompt-output-regexp nil t 1)) + (point-marker)) + (point-max))))) (mapconcat (lambda (string) string) (butlast (split-string output-buffer "\n")) "\n"))) @@ -1214,37 +1256,28 @@ else: return completions" "Code used to setup completion in inferior Python processes.") -(defvar python-shell-completion-strings-code +(defvar python-shell-completion-string-code "';'.join(__COMPLETER_all_completions('''%s'''))\n" "Python code used to get a string of completions separated by semicolons.") (defun python-shell-completion-setup () "Send `python-shell-completion-setup-code' to inferior Python process. -Also binds to `python-shell-complete-or-indent' in the -`inferior-python-mode-map' and adds -`python-shell-completion-complete-at-point' to the -`comint-dynamic-complete-functions' list. It is specially designed to be added to the `inferior-python-mode-hook'." - (when python-shell-completion-setup-code + (when (> (length python-shell-completion-setup-code) 0) (python-shell-send-string-no-output python-shell-completion-setup-code (get-buffer-process (current-buffer))) - (message "Completion setup code sent.") - (add-to-list (make-local-variable - 'comint-dynamic-complete-functions) - 'python-shell-completion-complete-at-point) - (define-key inferior-python-mode-map (kbd "") - 'python-shell-completion-complete-or-indent))) + (message "Completion setup code sent."))) (defun python-shell-completion--get-completions (input process) "Retrieve available completions for INPUT using PROCESS." (with-current-buffer (process-buffer process) - (split-string - (or (python-shell-send-string-no-output - (format python-shell-completion-strings-code input) - process) "") - ";\\|\"\\|'\\|(" t))) + (let ((completions (python-shell-send-string-no-output + (format python-shell-completion-string-code input) + process))) + (when (> (length completions) 2) + (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) (defun python-shell-completion--get-completion (input completions) "Get completion for INPUT using COMPLETIONS." @@ -1509,8 +1542,6 @@ the if condition." :regexp "\\(?:^\\|[^/]\\)\\<\\([[:word:]/]+\\)\\W*" ;; Only expand in code. :enable-function (lambda () - (message "ppss %s" (not (nth 8 (syntax-ppss)))) - (message "autoinsert %s" python-skeleton-autoinsert) (and (not (nth 8 (syntax-ppss))) python-skeleton-autoinsert))) @@ -1643,7 +1674,8 @@ The skeleton will be bound to python-skeleton-NAME." "Send `python-ffap-setup-code' to inferior Python process. It is specially designed to be added to the `inferior-python-mode-hook'." - (when python-ffap-setup-code + + (when (> (length python-ffap-setup-code) 0) (python-shell-send-string-no-output python-ffap-setup-code (get-buffer-process (current-buffer))) @@ -1725,7 +1757,7 @@ Runs COMMAND, a shell command, as if by `compile'. See "Send `python-eldoc-setup-code' to inferior Python process. It is specially designed to be added to the `inferior-python-mode-hook'." - (when python-eldoc-setup-code + (when (> (length python-eldoc-setup-code) 0) (python-shell-send-string-no-output python-eldoc-setup-code (get-buffer-process (current-buffer))) -- cgit v1.2.1 From 2db30ac50abaa2630285a24363be3ccda8889ce0 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:06 -0300 Subject: Use insert instead of insert-string --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6a5435afb24..650f030067c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1160,7 +1160,7 @@ Return the output." (> (length python-shell-prompt-output-regexp) 0)) (setq output-buffer (with-temp-buffer - (insert-string output-buffer) + (insert output-buffer) (goto-char (point-min)) (forward-comment 1) (buffer-substring-no-properties -- cgit v1.2.1 From da3d48d7d6a857c6a0da21c91685e951523c6b64 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:07 -0300 Subject: Remove find-file-noselect invocation in python-shell-send-file --- lisp/progmodes/python.el | 1 - 1 file changed, 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 650f030067c..8b7acc6b74c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1213,7 +1213,6 @@ FILE-NAME." (file-name (or (expand-file-name file-name) temp-file-name))) (when (not file-name) (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) - (find-file-noselect file-name) (with-current-buffer (process-buffer process) (setq inferior-python-mode-current-file (convert-standard-filename file-name))) -- cgit v1.2.1 From 099bf0104e2c6118189b64e79651f3bdfe9e09b2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:07 -0300 Subject: Added commentary about installing pyreadline on system that bundles Python without readline --- lisp/progmodes/python.el | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8b7acc6b74c..623614598d3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -72,6 +72,12 @@ ;; python-shell-completion-string-code ;; "';'.join(__IP.complete('''%s'''))\n") +;; Please note that the default completion system depends on the +;; readline module, so if you are using some Operating System that +;; bundles Python without it (like Windows) just install the +;; pyreadline from http://ipython.scipy.org/moin/PyReadline/Intro and +;; you should be good to go. + ;; Pdb tracking: when you execute a block of code that contains some ;; call to pdb (or ipdb) it will prompt the block of code and will ;; follow the execution of pdb marking the current line with an arrow. -- cgit v1.2.1 From 14a7849583a5d42aeb196226723cb9386aac2bba Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:07 -0300 Subject: New function python-info-ppss-context Use this function for most syntax-ppss related tasks. While in some parts code could be longer it makes everything more readable. This is the first step for a cleaner indentation machinery. --- lisp/progmodes/python.el | 75 +++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 27 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 623614598d3..ab711724ae7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -331,9 +331,9 @@ (? ?\[ (+ (not (any ?\]))) ?\]) (* space) assignment-operator))) (when (re-search-forward re limit t) - (while (and (not (equal (nth 0 (syntax-ppss)) 0)) + (while (and (python-info-ppss-context 'paren) (re-search-forward re limit t))) - (if (and (equal (nth 0 (syntax-ppss)) 0) + (if (and (not (python-info-ppss-context 'paren)) (not (equal (char-after (point-marker)) ?=))) t (set-match-data nil))))) @@ -346,10 +346,10 @@ assignment-operator))) (when (and (re-search-forward re limit t) (goto-char (nth 3 (match-data)))) - (while (and (not (equal (nth 0 (syntax-ppss)) 0)) + (while (and (python-info-ppss-context 'paren) (re-search-forward re limit t)) (goto-char (nth 3 (match-data)))) - (if (equal (nth 0 (syntax-ppss)) 0) + (if (not (python-info-ppss-context 'paren)) t (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)))) @@ -477,7 +477,8 @@ These make `python-indent-calculate-indentation' subtract the value of (while (and (not found-block) (re-search-forward (python-rx line-start block-start) nil t)) - (when (and (not (syntax-ppss-context (syntax-ppss))) + (when (and (not (python-info-ppss-context 'string)) + (not (python-info-ppss-context 'comment)) (progn (goto-char (line-end-position)) (forward-comment -1) @@ -527,14 +528,14 @@ START is the buffer position where the sexp starts." (bobp)) 'no-indent) ;; Inside a paren - ((setq start (nth 1 ppss)) + ((setq start (python-info-ppss-context 'paren ppss)) 'inside-paren) ;; Inside string - ((setq start (when (and (nth 3 ppss)) - (nth 8 ppss))) + ((setq start (python-info-ppss-context 'string ppss)) 'inside-string) ;; After backslash - ((setq start (when (not (syntax-ppss-context ppss)) + ((setq start (when (not (or (python-info-ppss-context 'string ppss) + (python-info-ppss-context 'comment ppss))) (let ((line-beg-pos (line-beginning-position))) (when (eq ?\\ (char-before (1- line-beg-pos))) (- line-beg-pos 2))))) @@ -570,7 +571,7 @@ START is the buffer position where the sexp starts." (while (and (forward-comment -1) (not (bobp)))) (while (and (not (back-to-indentation)) (not (bobp)) - (if (> (nth 0 (syntax-ppss)) 0) + (if (python-info-ppss-context 'paren) (forward-line -1) (if (save-excursion (forward-line -1) @@ -642,7 +643,7 @@ START is the buffer position where the sexp starts." (forward-comment 1) (looking-at (regexp-opt '(")" "]" "}"))) (forward-char 1) - (when (not (nth 1 (syntax-ppss))) + (when (not (python-info-ppss-context 'paren)) (goto-char context-start) (back-to-indentation) (current-column))) @@ -737,7 +738,8 @@ Internally just calls `python-indent-line'." (defun python-indent-dedent-line () "Dedent current line." (interactive "*") - (when (and (not (syntax-ppss-context (syntax-ppss))) + (when (and (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment))) (<= (point-marker) (save-excursion (back-to-indentation) (point-marker))) @@ -837,7 +839,8 @@ just insert a single colon." (self-insert-command (if (not (integerp arg)) 1 arg)) (and (not arg) (eolp) - (not (nth 8 (syntax-ppss))) + (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment))) (> (current-indentation) (python-indent-calculate-indentation)) (save-excursion (python-indent-line)))) (put 'python-indent-electric-colon 'delete-selection t) @@ -1454,18 +1457,19 @@ Optional argument JUSTIFY defines if the paragraph should be justified." ((fill-comment-paragraph justify)) ;; Docstrings ((save-excursion (skip-chars-forward "\"'uUrR") - (nth 3 (syntax-ppss))) + (python-info-ppss-context 'string)) (let ((marker (point-marker)) (string-start-marker (progn (skip-chars-forward "\"'uUrR") - (goto-char (nth 8 (syntax-ppss))) + (goto-char (python-info-ppss-context 'string)) (skip-chars-forward "\"'uUrR") (point-marker))) (reg-start (line-beginning-position)) (string-end-marker (progn - (while (nth 3 (syntax-ppss)) (goto-char (1+ (point-marker)))) + (while (python-info-ppss-context 'string) + (goto-char (1+ (point-marker)))) (skip-chars-backward "\"'") (point-marker))) (reg-end (line-end-position)) @@ -1496,23 +1500,23 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (back-to-indentation) (point-marker))) ?@) t) ;; Parens - ((or (> (nth 0 (syntax-ppss)) 0) + ((or (python-info-ppss-context 'paren) (looking-at (python-rx open-paren)) (save-excursion (skip-syntax-forward "^(" (line-end-position)) (looking-at (python-rx open-paren)))) (save-restriction (narrow-to-region (progn - (while (> (nth 0 (syntax-ppss)) 0) + (while (python-info-ppss-context 'paren) (goto-char (1- (point-marker)))) (point-marker) (line-beginning-position)) (progn - (when (not (> (nth 0 (syntax-ppss)) 0)) + (when (not (python-info-ppss-context 'paren)) (end-of-line) - (when (not (> (nth 0 (syntax-ppss)) 0)) + (when (not (python-info-ppss-context 'paren)) (skip-syntax-backward "^)"))) - (while (> (nth 0 (syntax-ppss)) 0) + (while (python-info-ppss-context 'paren) (goto-char (1+ (point-marker)))) (point-marker))) (let ((paragraph-start "\f\\|[ \t]*$") @@ -1548,7 +1552,8 @@ the if condition." ;; Only expand in code. :enable-function (lambda () (and - (not (nth 8 (syntax-ppss))) + (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment))) python-skeleton-autoinsert))) (defmacro python-skeleton-define (name doc &rest skel) @@ -1785,8 +1790,8 @@ will be used. If not FORCE-PROCESS is passed what (ppss (syntax-ppss)) (help (when (and input (not (string= input (concat current-defun "."))) - (eq nil (nth 3 ppss)) - (eq nil (nth 4 ppss))) + (not (or (python-info-ppss-context 'string ppss) + (python-info-ppss-context 'comment ppss)))) (when (string-match (concat (regexp-quote (concat current-defun ".")) "self\\.") input) @@ -1910,7 +1915,7 @@ not inside a defun." (save-excursion (let ((innermost-paren (progn (goto-char (line-end-position)) - (nth 1 (syntax-ppss))))) + (python-info-ppss-context 'paren)))) (when (and innermost-paren (and (<= (line-beginning-position) innermost-paren) (>= (line-end-position) innermost-paren))) @@ -1918,7 +1923,7 @@ not inside a defun." (looking-at (python-rx open-paren (* space) line-end))))) (save-excursion (back-to-indentation) - (nth 1 (syntax-ppss))))) + (python-info-ppss-context 'paren)))) (defun python-info-block-continuation-line-p () "Return non-nil if current line is a continuation of a block." @@ -1945,9 +1950,25 @@ not inside a defun." assignment-operator not-simple-operator) (line-end-position) t) - (not (syntax-ppss-context (syntax-ppss)))))) + (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment)))))) (point-marker)))) +(defun python-info-ppss-context (type &optional syntax-ppss) + "Return non-nil if point is on TYPE using SYNTAX-PPSS. +TYPE can be 'comment, 'string or 'parent. It returns the start +character address of the specified TYPE." + (let ((ppss (or syntax-ppss (syntax-ppss)))) + (case type + ('comment + (and (nth 4 ppss) + (nth 8 ppss))) + ('string + (nth 8 ppss)) + ('paren + (nth 1 ppss)) + (t nil)))) + ;;;###autoload (define-derived-mode python-mode fundamental-mode "Python" -- cgit v1.2.1 From c43cd8b10f278195c59fa641dca7670811e7c146 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:08 -0300 Subject: Enhancements to python-indent-electric-colon. Only de-indent line if it really closes a block. --- lisp/progmodes/python.el | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ab711724ae7..79ef752c0f2 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -830,19 +830,24 @@ lie." (setq count python-indent-offset)) (indent-rigidly start end count))) -;; Directly from Dave Love's python.el (defun python-indent-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) - (not (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment))) - (> (current-indentation) (python-indent-calculate-indentation)) - (save-excursion (python-indent-line)))) + (when (and (not arg) + (eolp) + (not (equal ?: (char-after (- (point-marker) 2)))) + (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment)))) + (let ((indentation (current-indentation)) + (calculated-indentation (python-indent-calculate-indentation))) + (when (> indentation calculated-indentation) + (save-excursion + (indent-line-to calculated-indentation) + (when (not (python-info-closing-block)) + (indent-line-to indentation))))))) (put 'python-indent-electric-colon 'delete-selection t) -- cgit v1.2.1 From 9f1537ef3e5e6782edacfacbf9f4396e3ab11bd1 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:08 -0300 Subject: Added indentation support for continuation of dotted expressions Expressions like these are now supported and indented correctly: Object.objects.exclude(foo=1)\ .filter(bar=2)\ .values_list('baz') Also added a small fix to python-info-assignment-continuation-line-p to check the match for the operator is not inside some paren. --- lisp/progmodes/python.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 79ef752c0f2..b745050f551 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -618,6 +618,17 @@ START is the buffer position where the sexp starts." (save-excursion (forward-line -1) (python-info-assignment-continuation-line-p))) + (dot-continuation + (save-excursion + (back-to-indentation) + (when (looking-at "\\.") + (forward-line -1) + (back-to-indentation) + (forward-char (length + (with-syntax-table python-dotty-syntax-table + (current-word)))) + (re-search-backward "\\." (line-beginning-position) t 1) + (current-column)))) (indentation (cond (block-continuation (goto-char block-continuation) (re-search-forward @@ -634,6 +645,8 @@ START is the buffer position where the sexp starts." (python-rx (* space)) (line-end-position) t) (current-column)) + (dot-continuation + dot-continuation) (t (goto-char context-start) (current-indentation))))) @@ -1956,6 +1969,7 @@ not inside a defun." not-simple-operator) (line-end-position) t) (not (or (python-info-ppss-context 'string) + (python-info-ppss-context 'paren) (python-info-ppss-context 'comment)))))) (point-marker)))) -- cgit v1.2.1 From c0428ba0bd8f0d9e4e05bc12aa7fce2df4e2dc11 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:09 -0300 Subject: Better shell setup using the new python-shell-send-setup-codes function. At the moment of shell setup, all the pending output is accepted so the prompt is always displayed correctly. --- lisp/progmodes/python.el | 74 +++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 42 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b745050f551..92a19309dc1 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -963,14 +963,14 @@ Returns nil if point is not in a def or class." (defcustom python-shell-interpreter "python" "Default Python interpreter for shell." - :group 'python :type 'string + :group 'python :safe 'stringp) (defcustom python-shell-interpreter-args "-i" "Default arguments for the Python interpreter." - :group 'python :type 'string + :group 'python :safe 'stringp) (defcustom python-shell-prompt-regexp ">>> " @@ -1001,6 +1001,18 @@ The regex should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) +(defcustom python-shell-setup-codes '(python-shell-completion-setup-code + python-ffap-setup-code + python-eldoc-setup-code) + "List of code run by `python-shell-send-setup-codes'. +Each variable can be either a simple string with the code to +execute or a cons with the form (CODE . DESCRIPTION), where CODE +is a string with the code to execute and DESCRIPTION is the +description of it." + :type '(repeat symbol) + :group 'python + :safe 'listp) + (defcustom python-shell-compilation-regexp-alist `((,(rx line-start (1+ (any " \t")) "File \"" (group (1+ (not (any "\"<")))) ; avoid `' &c @@ -1256,6 +1268,24 @@ FILE-NAME." (interactive) (pop-to-buffer (process-buffer (python-shell-get-or-create-process)) t)) +(defun python-shell-send-setup-code () + "Send all setup code for shell. +This function takes the list of setup code to send from the +`python-shell-setup-codes' list." + (let ((msg "Sent %s") + (process (get-buffer-process (current-buffer)))) + (accept-process-output process 1) + (dolist (code python-shell-setup-codes) + (when code + (when (consp code) + (setq msg (cdr code))) + (message (format msg code)) + (python-shell-send-string-no-output + (symbol-value code) process))))) + +(add-hook 'inferior-python-mode-hook + #'python-shell-send-setup-code) + ;;; Shell completion @@ -1286,16 +1316,6 @@ else: "';'.join(__COMPLETER_all_completions('''%s'''))\n" "Python code used to get a string of completions separated by semicolons.") -(defun python-shell-completion-setup () - "Send `python-shell-completion-setup-code' to inferior Python process. -It is specially designed to be added to the -`inferior-python-mode-hook'." - (when (> (length python-shell-completion-setup-code) 0) - (python-shell-send-string-no-output - python-shell-completion-setup-code - (get-buffer-process (current-buffer))) - (message "Completion setup code sent."))) - (defun python-shell-completion--get-completions (input process) "Retrieve available completions for INPUT using PROCESS." (with-current-buffer (process-buffer process) @@ -1349,9 +1369,6 @@ complete." (indent-for-tab-command) (comint-dynamic-complete))) -(add-hook 'inferior-python-mode-hook - #'python-shell-completion-setup) - ;;; PDB Track integration @@ -1698,17 +1715,6 @@ The skeleton will be bound to python-skeleton-NAME." "__FFAP_get_module_path('''%s''')\n" "Python code used to get a string with the path of a module.") -(defun python-ffap-setup () - "Send `python-ffap-setup-code' to inferior Python process. -It is specially designed to be added to the -`inferior-python-mode-hook'." - - (when (> (length python-ffap-setup-code) 0) - (python-shell-send-string-no-output - python-ffap-setup-code - (get-buffer-process (current-buffer))) - (message "FFAP setup code sent."))) - (defun python-ffap-module-path (module) "Function for `ffap-alist' to return path for MODULE." (let ((process (or @@ -1728,9 +1734,6 @@ It is specially designed to be added to the (push '(python-mode . python-ffap-module-path) ffap-alist) (push '(inferior-python-mode . python-ffap-module-path) ffap-alist))) -(add-hook 'inferior-python-mode-hook - #'python-ffap-setup) - ;;; Code check @@ -1781,16 +1784,6 @@ Runs COMMAND, a shell command, as if by `compile'. See "__PYDOC_get_help('''%s''')\n" "Python code used to get a string with the documentation of an object.") -(defun python-eldoc-setup () - "Send `python-eldoc-setup-code' to inferior Python process. -It is specially designed to be added to the -`inferior-python-mode-hook'." - (when (> (length python-eldoc-setup-code) 0) - (python-shell-send-string-no-output - python-eldoc-setup-code - (get-buffer-process (current-buffer))) - (message "Eldoc setup code sent."))) - (defun python-eldoc--get-doc-at-point (&optional force-input force-process) "Internal implementation to get documentation at point. If not FORCE-INPUT is passed then what `current-word' returns @@ -1862,9 +1855,6 @@ Interactively, prompt for symbol." (python-eldoc--get-doc-at-point symbol process)) (help-print-return-message))))))) -(add-hook 'inferior-python-mode-hook - #'python-eldoc-setup) - ;;; Misc helpers -- cgit v1.2.1 From e2d8d4790fa69c61976341e89e61eec558a4bcc7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:09 -0300 Subject: Documentation enhancements --- lisp/progmodes/python.el | 191 ++++++++++++++++++++++------------------------- 1 file changed, 90 insertions(+), 101 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 92a19309dc1..0a09ec55ef7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,6 +1,6 @@ ;;; python.el -- Python's flying circus support for Emacs -;; Copyright (C) 2010 Free Software Foundation, Inc. +;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. ;; Author: FabiĆ”n E. Gallina ;; Maintainer: FSF @@ -34,7 +34,7 @@ ;; Implements Syntax highlighting, Indentation, Movement, Shell ;; interaction, Shell completion, Pdb tracking, Symbol completion, -;; Eldoc. +;; Skeletons, FFAP, Code Check, Eldoc. ;; Syntax highlighting: Fontification of code is provided and supports ;; python's triple quoted strings properly. @@ -255,7 +255,8 @@ "=" "%" "**" "//" "<<" ">>" "<=" "!=" "==" ">=" "is" "not"))) `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" - ">>=" "<<=" "&=" "^=" "|=")))))) + ">>=" "<<=" "&=" "^=" "|=")))) + "Additional Python specific sexps for `python-rx'")) (defmacro python-rx (&rest regexps) "Python mode especialized rx macro which supports common python named REGEXPS." @@ -498,23 +499,18 @@ These make `python-indent-calculate-indentation' subtract the value of (when (> indent-offset 0) (setq python-indent-offset indent-offset)))))))) -(defun python-indent-context (&optional stop) - "Return information on indentation context. -Optional argument STOP serves to stop recursive calls. - -Returns a cons with the form: - -\(STATUS . START) +(defun python-indent-context () + "Get information on indentation context. +Context information is returned with a cons with the form: + \(STATUS . START) Where status can be any of the following symbols: - * inside-paren: If point in between (), {} or [] * inside-string: If point is inside a string * after-backslash: Previous line ends in a backslash * after-beginning-of-block: Point is after beginning of block * after-line: Point is after normal line * no-indent: Point is at beginning of buffer or other special case - START is the buffer position where the sexp starts." (save-restriction (widen) @@ -704,27 +700,23 @@ START is the buffer position where the sexp starts." (defun python-indent-line (&optional force-toggle) "Internal implementation of `python-indent-line-function'. - Uses the offset calculated in `python-indent-calculate-indentation' and available levels -indicated by the variable `python-indent-levels'. +indicated by the variable `python-indent-levels' to set the +current indentation. When the variable `last-command' is equal to -`indent-for-tab-command' or FORCE-TOGGLE is non-nil: - -* Cycles levels indicated in the variable `python-indent-levels' - by setting the current level in the variable - `python-indent-current-level'. +`indent-for-tab-command' or FORCE-TOGGLE is non-nil it cycles +levels indicated in the variable `python-indent-levels' by +setting the current level in the variable +`python-indent-current-level'. When the variable `last-command' is not equal to -`indent-for-tab-command' and FORCE-TOGGLE is nil: - -* calculates possible indentation levels and saves it in the - variable `python-indent-levels'. - -* sets the variable `python-indent-current-level' correctly so - offset is equal to (`nth' `python-indent-current-level' - `python-indent-levels')" +`indent-for-tab-command' and FORCE-TOGGLE is nil it calculates +possible indentation levels and saves it in the variable +`python-indent-levels'. Afterwards it sets the variable +`python-indent-current-level' correctly so offset is equal +to (`nth' `python-indent-current-level' `python-indent-levels')" (if (or (and (eq this-command 'indent-for-tab-command) (eq last-command this-command)) force-toggle) @@ -745,11 +737,11 @@ When the variable `last-command' is not equal to (defun python-indent-line-function () "`indent-line-function' for Python mode. -Internally just calls `python-indent-line'." +See `python-indent-line' for details." (python-indent-line)) (defun python-indent-dedent-line () - "Dedent current line." + "De-indent current line." (interactive "*") (when (and (not (or (python-info-ppss-context 'string) (python-info-ppss-context 'comment))) @@ -761,9 +753,9 @@ Internally just calls `python-indent-line'." t)) (defun python-indent-dedent-line-backspace (arg) - "Dedent current line. + "De-indent current line. Argument ARG is passed to `backward-delete-char-untabify' when -point is not in between the indentation." +point is not in between the indentation." (interactive "*p") (when (not (python-indent-dedent-line)) (backward-delete-char-untabify arg))) @@ -795,15 +787,10 @@ Called from a program, START and END specify the region to indent." (defun python-indent-shift-left (start end &optional count) "Shift lines contained in region START END by COUNT columns to the left. - -COUNT defaults to `python-indent-offset'. - -If region isn't active, the current line is shifted. - -The shifted region includes the lines in which START and END lie. - -An error is signaled if any lines in the region are indented less -than COUNT columns." +COUNT defaults to `python-indent-offset'. If region isn't +active, the current line is shifted. The shifted region includes +the lines in which START and END lie. An error is signaled if +any lines in the region are indented less than COUNT columns." (interactive (if mark-active (list (region-beginning) (region-end) current-prefix-arg) @@ -826,13 +813,9 @@ than COUNT columns." (defun python-indent-shift-right (start end &optional count) "Shift lines contained in region START END by COUNT columns to the left. - -COUNT defaults to `python-indent-offset'. - -If region isn't active, the current line is shifted. - -The shifted region includes the lines in which START and END -lie." +COUNT defaults to `python-indent-offset'. If region isn't +active, the current line is shifted. The shifted region includes +the lines in which START and END lie." (interactive (if mark-active (list (region-beginning) (region-end) current-prefix-arg) @@ -844,9 +827,9 @@ lie." (indent-rigidly start end count))) (defun python-indent-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." + "Insert a colon and maybe de-indent the current line. +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)) (when (and (not arg) @@ -974,29 +957,29 @@ Returns nil if point is not in a def or class." :safe 'stringp) (defcustom python-shell-prompt-regexp ">>> " - "Regex matching top\-level input prompt of python shell. -The regex should not contain a caret (^) at the beginning." + "Regular Expression matching top\-level input prompt of python shell. +It should not contain a caret (^) at the beginning." :type 'string :group 'python :safe 'stringp) (defcustom python-shell-prompt-block-regexp "[.][.][.] " - "Regex matching block input prompt of python shell. -The regex should not contain a caret (^) at the beginning." + "Regular Expression matching block input prompt of python shell. +It should not contain a caret (^) at the beginning." :type 'string :group 'python :safe 'stringp) (defcustom python-shell-prompt-output-regexp nil - "Regex matching output prompt of python shell. -The regex should not contain a caret (^) at the beginning." + "Regular Expression matching output prompt of python shell. +It should not contain a caret (^) at the beginning." :type 'string :group 'python :safe 'stringp) (defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " - "Regex matching pdb input prompt of python shell. -The regex should not contain a caret (^) at the beginning." + "Regular Expression matching pdb input prompt of python shell. +It should not contain a caret (^) at the beginning." :type 'string :group 'python :safe 'stringp) @@ -1005,7 +988,7 @@ The regex should not contain a caret (^) at the beginning." python-ffap-setup-code python-eldoc-setup-code) "List of code run by `python-shell-send-setup-codes'. -Each variable can be either a simple string with the code to +Each variable can contain either a simple string with the code to execute or a cons with the form (CODE . DESCRIPTION), where CODE is a string with the code to execute and DESCRIPTION is the description of it." @@ -1030,14 +1013,12 @@ description of it." (defun python-shell-get-process-name (dedicated) "Calculate the appropiate process name for inferior Python process. - If DEDICATED is t and the variable `buffer-file-name' is non-nil returns a string with the form `python-shell-buffer-name'[variable `buffer-file-name'] else -returns the value of `python-shell-buffer-name'. - -After calculating the process name add the buffer name for the -process in the `same-window-buffer-names' list" +returns the value of `python-shell-buffer-name'. After +calculating the process name adds the buffer name for the process +in the `same-window-buffer-names' list." (let ((process-name (if (and dedicated buffer-file-name) @@ -1048,7 +1029,7 @@ process in the `same-window-buffer-names' list" process-name)) (defun python-shell-parse-command () - "Calculates the string used to execute the inferior Python process." + "Calculate the string used to execute the inferior Python process." (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) (defun python-comint-output-filter-function (output) @@ -1062,10 +1043,24 @@ OUTPUT is a string with the contents of the buffer." (define-derived-mode inferior-python-mode comint-mode "Inferior Python" "Major mode for Python inferior process. -Adds `python-shell-completion-complete-at-point' to the -`comint-dynamic-complete-functions' list. Also binds to -`python-shell-complete-or-indent' in the -`inferior-python-mode-map'." +Runs a Python interpreter as a subprocess of Emacs, with Python +I/O through an Emacs buffer. Variables +`python-shell-interpreter' and `python-shell-interpreter-args' +controls which Python interpreter is run. Variables +`python-shell-prompt-regexp', +`python-shell-prompt-output-regexp', +`python-shell-prompt-block-regexp', +`python-shell-completion-setup-code', +`python-shell-completion-string-code', `python-eldoc-setup-code', +`python-eldoc-string-code', `python-ffap-setup-code' and +`python-ffap-string-code' can customize this mode for different +Python interpreters. + +You can also add additional setup code to be run at +initialization of the interpreter via `python-shell-setup-codes' +variable. + +\(Type \\[describe-mode] in the process buffer for a list of commands.)" (set-syntax-table python-mode-syntax-table) (setq mode-line-process '(":%s")) (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" @@ -1091,26 +1086,17 @@ Adds `python-shell-completion-complete-at-point' to the (defun run-python (dedicated cmd) "Run an inferior Python process. - -Input and output via buffer *\\[python-shell-buffer-name]*. - -If there is a process already running in -*\\[python-shell-buffer-name]*, switch to that buffer. - -With argument, allows you to: - - * Define DEDICATED so a dedicated process for the current buffer - is open. - - * Define CMD so you can edit the command used to call the -interpreter (default is value of `python-shell-interpreter' and -arguments defined in `python-shell-interpreter-args'). - -Runs the hook `inferior-python-mode-hook' (after the -`comint-mode-hook' is run). - -\(Type \\[describe-mode] in the process buffer for a list of -commands.)" +Input and output via buffer named after +`python-shell-buffer-name'. If there is a process already +running in that buffer, just switch to it. +With argument, allows you to define DEDICATED, so a dedicated +process for the current buffer is open, and define CMD so you can +edit the command used to call the interpreter (default is value +of `python-shell-interpreter' and arguments defined in +`python-shell-interpreter-args'). Runs the hook +`inferior-python-mode-hook' (after the `comint-mode-hook' is +run). +\(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive (if current-prefix-arg (list @@ -1182,8 +1168,8 @@ When MSG is non-nil messages the first line of STRING." (defun python-shell-send-string-no-output (string &optional process msg) "Send STRING to PROCESS and inhibit output. -When MSG is non-nil messages the first line of STRING. -Return the output." +When MSG is non-nil messages the first line of STRING. Return +the output." (let* ((output-buffer) (process (or process (python-shell-get-or-create-process))) (comint-preoutput-filter-functions @@ -1360,8 +1346,8 @@ else: (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. -If content before pointer is all whitespace indent. If not try to -complete." +If content before pointer is all whitespace indent. If not try +to complete." (interactive) (if (string-match "^[[:space:]]*$" (buffer-substring (comint-line-beginning-position) @@ -1374,12 +1360,11 @@ complete." (defvar python-pdbtrack-stacktrace-info-regexp "> %s(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" - "Regexp matching stacktrace information. -It is used to extract the current line and module beign -inspected. -The regexp should not start with a caret (^) and can contain a -string placeholder (\%s) which is replaced with the filename -beign inspected (so other files in the debugging process are not + "Regular Expression matching stacktrace information. +Used to extract the current line and module beign inspected. The +regexp should not start with a caret (^) and can contain a string +placeholder (\%s) which is replaced with the filename beign +inspected (so other files in the debugging process are not opened)") (defvar python-pdbtrack-tracking-buffers '() @@ -1879,7 +1864,7 @@ not inside a defun." (mapconcat (lambda (string) string) names ".")))) (defun python-info-closing-block () - "Return the point of the block that the current line closes." + "Return the point of the block the current line closes." (let ((closing-word (save-excursion (back-to-indentation) (current-word))) @@ -1981,7 +1966,11 @@ character address of the specified TYPE." ;;;###autoload (define-derived-mode python-mode fundamental-mode "Python" - "A major mode for editing Python files." + "Major mode for editing Python files. + +\\{python-mode-map} +Entry to this mode calls the value of `python-mode-hook' +if that value is non-nil." (set (make-local-variable 'tab-width) 8) (set (make-local-variable 'indent-tabs-mode) nil) -- cgit v1.2.1 From 30e429dd37d8a97afcf0129f8fe498438a611d65 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:09 -0300 Subject: New variable python-shell-send-setup-max-wait Controls the timeout for output just before sending the setup code. --- lisp/progmodes/python.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0a09ec55ef7..94035546c6e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -984,6 +984,14 @@ It should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) +(defcustom python-shell-send-setup-max-wait 5 + "Seconds to wait for process output before code setup. +If output is received before the especified time then control is +returned in that moment and not after waiting." + :type 'number + :group 'python + :safe 'numberp) + (defcustom python-shell-setup-codes '(python-shell-completion-setup-code python-ffap-setup-code python-eldoc-setup-code) @@ -1260,7 +1268,7 @@ This function takes the list of setup code to send from the `python-shell-setup-codes' list." (let ((msg "Sent %s") (process (get-buffer-process (current-buffer)))) - (accept-process-output process 1) + (accept-process-output process python-shell-send-setup-max-wait) (dolist (code python-shell-setup-codes) (when code (when (consp code) -- cgit v1.2.1 From 66bbb27f8dc210f81beec6a11a8613efefd1d5ad Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:10 -0300 Subject: new variables python-shell-process-environment and python-shell-exec-path The main reason for these new variables is virtualenv support. --- lisp/progmodes/python.el | 52 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 94035546c6e..dc7e46dc129 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -78,6 +78,26 @@ ;; pyreadline from http://ipython.scipy.org/moin/PyReadline/Intro and ;; you should be good to go. +;; The shell also contains support for virtualenvs and other special +;; environment modification thanks to +;; `python-shell-process-environment' and `python-shell-exec-path'. +;; These two variables allows you to modify execution paths and +;; enviroment variables to make easy for you to setup virtualenv rules +;; or behaviors modifications when running shells. Here is an example +;; of how to make shell processes to be run using the /path/to/env/ +;; virtualenv: + +;; (setq python-shell-process-environment +;; (list +;; (format "PATH=%s" (mapconcat +;; 'identity +;; (reverse +;; (cons (getenv "PATH") +;; '("/path/to/env/bin/"))) +;; ":")) +;; "VIRTUAL_ENV=/path/to/env/")) +;; (python-shell-exec-path . ("/path/to/env/bin/")) + ;; Pdb tracking: when you execute a block of code that contains some ;; call to pdb (or ipdb) it will prompt the block of code and will ;; follow the execution of pdb marking the current line with an arrow. @@ -992,6 +1012,26 @@ returned in that moment and not after waiting." :group 'python :safe 'numberp) +(defcustom python-shell-process-environment nil + "List of enviroment variables for Python shell. +This variable follows the same rules as `process-enviroment' +since it merges with it before the process creation routines are +called. When this variable is nil, the Python shell is run with +the default `process-enviroment'." + :type '(repeat string) + :group 'python + :safe 'listp) + +(defcustom python-shell-exec-path nil + "List of path to search for binaries. +This variable follows the same rules as `exec-path' since it +merges with it before the process creation routines are called. +When this variable is nil, the Python shell is run with the +default `exec-path'." + :type '(repeat string) + :group 'python + :safe 'listp) + (defcustom python-shell-setup-codes '(python-shell-completion-setup-code python-ffap-setup-code python-eldoc-setup-code) @@ -1112,7 +1152,17 @@ run). (read-string "Run Python: " (python-shell-parse-command))) (list nil (python-shell-parse-command)))) (let* ((proc-name (python-shell-get-process-name dedicated)) - (proc-buffer-name (format "*%s*" proc-name))) + (proc-buffer-name (format "*%s*" proc-name)) + (process-environment + (if python-shell-process-environment + (merge 'list python-shell-process-environment + process-environment 'string=) + process-environment)) + (exec-path + (if python-shell-exec-path + (merge 'list python-shell-exec-path + exec-path 'string=) + exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let ((cmdlist (split-string-and-unquote cmd))) (set-buffer -- cgit v1.2.1 From 86f1889aa95d179c6d46fbb138eb47578c9aa372 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:10 -0300 Subject: Fixed indentation issue when indenting a python-indent-dedenter from end of line --- lisp/progmodes/python.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index dc7e46dc129..b6bb825fc1a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -700,8 +700,7 @@ START is the buffer position where the sexp starts." (let* ((indentation (python-indent-calculate-indentation)) (remainder (% indentation python-indent-offset)) (steps (/ (- indentation remainder) python-indent-offset))) - (setq python-indent-levels '()) - (setq python-indent-levels (cons 0 python-indent-levels)) + (setq python-indent-levels '(0)) (dotimes (step steps) (setq python-indent-levels (cons (* python-indent-offset (1+ step)) python-indent-levels))) @@ -740,7 +739,9 @@ to (`nth' `python-indent-current-level' `python-indent-levels')" (if (or (and (eq this-command 'indent-for-tab-command) (eq last-command this-command)) force-toggle) - (python-indent-toggle-levels) + (if (not (equal python-indent-levels '(0))) + (python-indent-toggle-levels) + (python-indent-calculate-levels)) (python-indent-calculate-levels)) (beginning-of-line) (delete-horizontal-space) -- cgit v1.2.1 From f9471190dcdeb216c76771bea4483989291b1bbd Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:10 -0300 Subject: Better indentation handling when inside parens. --- lisp/progmodes/python.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b6bb825fc1a..dc9e091603b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -670,9 +670,9 @@ START is the buffer position where the sexp starts." ('inside-paren (or (save-excursion (forward-comment 1) - (looking-at (regexp-opt '(")" "]" "}"))) - (forward-char 1) - (when (not (python-info-ppss-context 'paren)) + (when (and (looking-at (regexp-opt '(")" "]" "}"))) + (not (forward-char 1)) + (not (python-info-ppss-context 'paren))) (goto-char context-start) (back-to-indentation) (current-column))) -- cgit v1.2.1 From c806ea6e87c11d32840844798a2bd6166d1a72e1 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:11 -0300 Subject: New variable python-use-beginning-of-innermost-defun Controls if beginning-of-defun function should go to outermost or innermost defun. --- lisp/progmodes/python.el | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index dc9e091603b..0213a0ec8fb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -870,6 +870,12 @@ With numeric ARG, just insert that many colons. With ;;; Navigation +(defcustom python-use-beginning-of-innermost-defun nil + "Set if `beginning-of-defun-function' should go to innermost defun." + :type 'string + :group 'python + :safe 'stringp) + (defvar python-beginning-of-defun-regexp "^\\(def\\|class\\)[[:space:]]+[[:word:]]+" "Regular expresion matching beginning of outermost class or function.") @@ -919,8 +925,17 @@ innermost definition." nil)))) (defun python-beginning-of-defun-function () + "Move point to the beginning of \(inner|outer)most def or class. +The point is moved to the beginning of innermost or outermost def +or class given the value of +`python-use-beginning-of-innermost-defun'. Returns nil if point +is not in a def or class." + (python-beginning-of-defun python-use-beginning-of-innermost-defun)) + +(defun python-beginning-of-outermost-defun () "Move point to the beginning of outermost def or class. Returns nil if point is not in a def or class." + (interactive) (python-beginning-of-defun nil)) (defun python-beginning-of-innermost-defun () -- cgit v1.2.1 From 0567effbae17635607455d23be90da9d02d744c7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:11 -0300 Subject: Corrected implementations for python-{beginning,end}-of-defun functions. When point is at beginning-of-defun, end-of-defun moves to the end of the defun, even if it contains nested defuns. When point is at any inner defun end-of-defun moves to the end of it, if another inner defun exists at the same level point is moved to it. For beginning-of-defun things are funkier, it would move backwards following nested defuns in order. This will be fixed soon. Note: Decorators are considered part of defuns. Removed: * python-use-beginning-of-innermost-defun * python-beginning-of-innermost-defun-regexp Renamed: * python-beginning-of-defun => python-nav-beginning-of-defun * python-beginning-of-defun-regexp => python-nav-beginning-of-defun-regexp --- lisp/progmodes/python.el | 164 ++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 100 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0213a0ec8fb..bbf66f2033f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -46,8 +46,7 @@ ;; causes the current line to be dedented automatically if needed. ;; Movement: `beginning-of-defun' and `end-of-defun' functions are -;; properly implemented. A `beginning-of-innermost-defun' is defined -;; to navigate nested defuns. +;; properly implemented. ;; Shell interaction: is provided and allows you easily execute any ;; block of code of your current buffer in an inferior Python process. @@ -224,8 +223,6 @@ "-" ["Start of def/class" beginning-of-defun :help "Go to start of outermost definition around point"] - ["Start of def/class" python-beginning-of-innermost-defun - :help "Go to start of innermost definition around point"] ["End of def/class" end-of-defun :help "Go to end of definition around point"] "-" @@ -266,7 +263,10 @@ (or "def" "class" "if" "elif" "else" "try" "except" "finally" "for" "while" "with") symbol-end)) + `(decorator . ,(rx line-start (* space) ?@ (any letter ?_) + (* (any word ?_)))) `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) + `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) `(open-paren . ,(rx (or "{" "[" "("))) `(close-paren . ,(rx (or "}" "]" ")"))) `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) @@ -870,109 +870,73 @@ With numeric ARG, just insert that many colons. With ;;; Navigation -(defcustom python-use-beginning-of-innermost-defun nil - "Set if `beginning-of-defun-function' should go to innermost defun." - :type 'string - :group 'python - :safe 'stringp) - -(defvar python-beginning-of-defun-regexp - "^\\(def\\|class\\)[[:space:]]+[[:word:]]+" - "Regular expresion matching beginning of outermost class or function.") - -(defvar python-beginning-of-innermost-defun-regexp - "^[[:space:]]*\\(def\\|class\\)[[:space:]]+[[:word:]]+" +(defvar python-nav-beginning-of-defun-regexp + (python-rx line-start (* space) defun (+ space) symbol-name) "Regular expresion matching beginning of innermost class or function.") -(defun python-beginning-of-defun (&optional innermost) - "Move point to the beginning of innermost/outermost def or class. -If INNERMOST is non-nil then move to the beginning of the -innermost definition." - (let ((starting-point (point-marker)) - (nonblank-line-indent) - (defun-indent) - (defun-point) - (regexp (if innermost - python-beginning-of-innermost-defun-regexp - python-beginning-of-defun-regexp))) - (back-to-indentation) - (if (and (not (looking-at "@")) - (not (looking-at regexp))) - (forward-comment -1) - (while (and (not (eobp)) - (forward-line 1) - (not (back-to-indentation)) - (looking-at "@")))) - (when (not (looking-at regexp)) - (re-search-backward regexp nil t)) - (setq nonblank-line-indent (+ (current-indentation) python-indent-offset)) - (setq defun-indent (current-indentation)) - (setq defun-point (point-marker)) - (if (> nonblank-line-indent defun-indent) +(defun python-nav-beginning-of-defun () + "Move point to beginning-of-defun. +This is the main part of`python-beginning-of-defun-function' +implementation." + (let ((indent-pos (save-excursion + (back-to-indentation) + (point-marker))) + (include-decorators + (lambda () + (when (save-excursion + (forward-line -1) + (looking-at (python-rx decorator))) + (while (and (not (bobp)) + (forward-line -1) + (looking-at (python-rx decorator)))) + (when (not (bobp)) (forward-line 1)))))) + (if (and (> (point) indent-pos) + (save-excursion + (goto-char (line-beginning-position)) + (looking-at python-nav-beginning-of-defun-regexp))) (progn - (goto-char defun-point) - (forward-line -1) - (while (and (looking-at "@") - (forward-line -1) - (not (bobp)) - (not (back-to-indentation)))) - (unless (bobp) - (forward-line 1)) - (point-marker)) - (if innermost - (python-beginning-of-defun) - (goto-char starting-point) - nil)))) - -(defun python-beginning-of-defun-function () - "Move point to the beginning of \(inner|outer)most def or class. -The point is moved to the beginning of innermost or outermost def -or class given the value of -`python-use-beginning-of-innermost-defun'. Returns nil if point -is not in a def or class." - (python-beginning-of-defun python-use-beginning-of-innermost-defun)) - -(defun python-beginning-of-outermost-defun () - "Move point to the beginning of outermost def or class. -Returns nil if point is not in a def or class." - (interactive) - (python-beginning-of-defun nil)) - -(defun python-beginning-of-innermost-defun () - "Move point to the beginning of innermost def or class. -Returns nil if point is not in a def or class." - (interactive) - (python-beginning-of-defun t)) + (goto-char (line-beginning-position)) + (funcall include-decorators)) + (goto-char (line-beginning-position)) + (re-search-backward python-nav-beginning-of-defun-regexp nil t) + (goto-char (or (python-info-ppss-context 'string) (point))) + (funcall include-decorators)))) + +(defun python-beginning-of-defun-function (&optional arg) + "Move point to the beginning of def or class. +With positive ARG move that number of functions forward. With +negative do the same but backwards." + (when (or (null arg) (= arg 0)) (setq arg 1)) + (if (> arg 0) + (dotimes (i arg) + (python-nav-beginning-of-defun)) + (dotimes (i (- arg)) + (python-end-of-defun-function) + (forward-comment 1) + (goto-char (line-end-position)) + (when (not (eobp)) + (python-nav-beginning-of-defun))))) (defun python-end-of-defun-function () "Move point to the end of def or class. Returns nil if point is not in a def or class." - (let ((starting-point (point-marker)) - (defun-regexp (python-rx defun)) - (beg-defun-indent)) - (back-to-indentation) - (if (looking-at "@") - (while (and (not (eobp)) - (forward-line 1) - (not (back-to-indentation)) - (looking-at "@"))) - (while (and (not (bobp)) - (not (progn (back-to-indentation) (current-word))) - (forward-line -1)))) - (when (or (not (equal (current-indentation) 0)) - (string-match defun-regexp (current-word))) - (setq beg-defun-indent (save-excursion - (or (looking-at defun-regexp) - (python-beginning-of-innermost-defun)) - (current-indentation))) - (while (and (forward-line 1) - (not (eobp)) - (or (not (current-word)) - (> (current-indentation) beg-defun-indent)))) - (while (and (forward-comment -1) - (not (bobp)))) - (forward-line 1) - (point-marker)))) + (interactive) + (let ((beg-defun-indent) + (decorator-regexp "[[:space:]]*@")) + (when (looking-at decorator-regexp) + (while (and (not (eobp)) + (forward-line 1) + (looking-at decorator-regexp)))) + (when (not (looking-at python-nav-beginning-of-defun-regexp)) + (python-beginning-of-defun-function)) + (setq beg-defun-indent (current-indentation)) + (forward-line 1) + (while (and (forward-line 1) + (not (eobp)) + (or (not (current-word)) + (> (current-indentation) beg-defun-indent)))) + (forward-comment 1) + (goto-char (line-beginning-position)))) ;;; Shell integration -- cgit v1.2.1 From c2cb97aec2d940c21629b7872fafc9998f673dae Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:11 -0300 Subject: User customizable fill-paragraph behavior. For this, four new variables which contain the symbol name of the function that specifies the behavior of fill-paragraph on certain conditions were added: * python-fill-comment-function: For comments * python-fill-string-function: For strings * python-fill-decorator-function: For decorators * python-fill-paren-function: For parens All of these variables are safe local variables in the case the value provided is a symbol. Out of the box, they default to these four new functions respectively: * python-fill-comment * python-fill-string * python-fill-decorator * python-fill-paren --- lisp/progmodes/python.el | 175 ++++++++++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 63 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index bbf66f2033f..be5221cee28 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1501,6 +1501,38 @@ inferior python process is updated properly." ;;; Fill paragraph +(defcustom python-fill-comment-function 'python-fill-comment + "Function to fill comments. +This is the function used by `python-fill-paragraph-function' to +fill comments." + :type 'symbol + :group 'python + :safe 'symbolp) + +(defcustom python-fill-string-function 'python-fill-string + "Function to fill strings. +This is the function used by `python-fill-paragraph-function' to +fill strings." + :type 'symbol + :group 'python + :safe 'symbolp) + +(defcustom python-fill-decorator-function 'python-fill-decorator + "Function to fill decorators. +This is the function used by `python-fill-paragraph-function' to +fill decorators." + :type 'symbol + :group 'python + :safe 'symbolp) + +(defcustom python-fill-paren-function 'python-fill-paren + "Function to fill parens. +This is the function used by `python-fill-paragraph-function' to +fill parens." + :type 'symbol + :group 'python + :safe 'symbolp) + (defun python-fill-paragraph-function (&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, @@ -1512,82 +1544,99 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (back-to-indentation) (cond ;; Comments - ((fill-comment-paragraph justify)) - ;; Docstrings + ((funcall python-fill-comment-function justify)) + ;; Strings/Docstrings ((save-excursion (skip-chars-forward "\"'uUrR") (python-info-ppss-context 'string)) - (let ((marker (point-marker)) - (string-start-marker - (progn - (skip-chars-forward "\"'uUrR") - (goto-char (python-info-ppss-context 'string)) - (skip-chars-forward "\"'uUrR") - (point-marker))) - (reg-start (line-beginning-position)) - (string-end-marker - (progn - (while (python-info-ppss-context 'string) - (goto-char (1+ (point-marker)))) - (skip-chars-backward "\"'") - (point-marker))) - (reg-end (line-end-position)) - (fill-paragraph-function)) - (save-restriction - (narrow-to-region reg-start reg-end) - (save-excursion - (goto-char string-start-marker) - (delete-region (point-marker) (progn - (skip-syntax-forward "> ") - (point-marker))) - (goto-char string-end-marker) - (delete-region (point-marker) (progn - (skip-syntax-backward "> ") - (point-marker))) - (save-excursion - (goto-char marker) - (fill-paragraph justify)) - ;; If there is a newline in the docstring lets put triple - ;; quote in it's own line to follow pep 8 - (when (save-excursion - (re-search-backward "\n" string-start-marker t)) - (newline) - (newline-and-indent)) - (fill-paragraph justify)))) t) + (funcall python-fill-string-function justify)) ;; Decorators ((equal (char-after (save-excursion (back-to-indentation) - (point-marker))) ?@) t) + (point-marker))) ?@) + (funcall python-fill-decorator-function justify)) ;; Parens ((or (python-info-ppss-context 'paren) (looking-at (python-rx open-paren)) (save-excursion (skip-syntax-forward "^(" (line-end-position)) (looking-at (python-rx open-paren)))) - (save-restriction - (narrow-to-region (progn - (while (python-info-ppss-context 'paren) - (goto-char (1- (point-marker)))) - (point-marker) - (line-beginning-position)) - (progn - (when (not (python-info-ppss-context 'paren)) - (end-of-line) - (when (not (python-info-ppss-context 'paren)) - (skip-syntax-backward "^)"))) - (while (python-info-ppss-context 'paren) - (goto-char (1+ (point-marker)))) - (point-marker))) - (let ((paragraph-start "\f\\|[ \t]*$") - (paragraph-separate ",") - (fill-paragraph-function)) - (goto-char (point-min)) - (fill-paragraph justify)) - (while (not (eobp)) - (forward-line 1) - (python-indent-line) - (goto-char (line-end-position)))) t) + (funcall python-fill-paren-function justify)) (t t)))) +(defun python-fill-comment (&optional justify) + "Comment fill function for `python-fill-paragraph-function'." + (fill-comment-paragraph justify)) + +(defun python-fill-string (&optional justify) + "String fill function for `python-fill-paragraph-function'." + (let ((marker (point-marker)) + (string-start-marker + (progn + (skip-chars-forward "\"'uUrR") + (goto-char (python-info-ppss-context 'string)) + (skip-chars-forward "\"'uUrR") + (point-marker))) + (reg-start (line-beginning-position)) + (string-end-marker + (progn + (while (python-info-ppss-context 'string) + (goto-char (1+ (point-marker)))) + (skip-chars-backward "\"'") + (point-marker))) + (reg-end (line-end-position)) + (fill-paragraph-function)) + (save-restriction + (narrow-to-region reg-start reg-end) + (save-excursion + (goto-char string-start-marker) + (delete-region (point-marker) (progn + (skip-syntax-forward "> ") + (point-marker))) + (goto-char string-end-marker) + (delete-region (point-marker) (progn + (skip-syntax-backward "> ") + (point-marker))) + (save-excursion + (goto-char marker) + (fill-paragraph justify)) + ;; If there is a newline in the docstring lets put triple + ;; quote in it's own line to follow pep 8 + (when (save-excursion + (re-search-backward "\n" string-start-marker t)) + (newline) + (newline-and-indent)) + (fill-paragraph justify)))) t) + +(defun python-fill-decorator (&optional justify) + "Decorator fill function for `python-fill-paragraph-function'." + t) + +(defun python-fill-paren (&optional justify) + "Paren fill function for `python-fill-paragraph-function'." + (save-restriction + (narrow-to-region (progn + (while (python-info-ppss-context 'paren) + (goto-char (1- (point-marker)))) + (point-marker) + (line-beginning-position)) + (progn + (when (not (python-info-ppss-context 'paren)) + (end-of-line) + (when (not (python-info-ppss-context 'paren)) + (skip-syntax-backward "^)"))) + (while (python-info-ppss-context 'paren) + (goto-char (1+ (point-marker)))) + (point-marker))) + (let ((paragraph-start "\f\\|[ \t]*$") + (paragraph-separate ",") + (fill-paragraph-function)) + (goto-char (point-min)) + (fill-paragraph justify)) + (while (not (eobp)) + (forward-line 1) + (python-indent-line) + (goto-char (line-end-position)))) t) + ;;; Skeletons -- cgit v1.2.1 From 2ed294c5df967cf4700539f8e2842873813051cc Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:12 -0300 Subject: Replaced references from python-beginning-of-innermost-defun to python-beginning-of-defun-function --- lisp/progmodes/python.el | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index be5221cee28..6295dad559e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1251,18 +1251,17 @@ the output." (python-shell-send-region (point-min) (point-max)))) (defun python-shell-send-defun (arg) - "Send the (inner|outer)most def or class to inferior Python process. + "Send the current defun to inferior Python process. When argument ARG is non-nil sends the innermost defun." (interactive "P") (save-excursion - (python-shell-send-region (progn - (or (if arg - (python-beginning-of-innermost-defun) - (python-beginning-of-defun-function)) - (progn (beginning-of-line) (point-marker)))) - (progn - (or (python-end-of-defun-function) - (progn (end-of-line) (point-marker))))))) + (python-shell-send-region + (progn + (or (python-beginning-of-defun-function) + (progn (beginning-of-line) (point-marker)))) + (progn + (or (python-end-of-defun-function) + (progn (end-of-line) (point-marker))))))) (defun python-shell-send-file (file-name &optional process temp-file-name) "Send FILE-NAME to inferior Python PROCESS. @@ -1943,7 +1942,7 @@ not inside a defun." (when (not (>= (current-indentation) python-indent-offset)) (while (and (not (eobp)) (forward-comment 1)))) (while (and (not (equal 0 (current-indentation))) - (python-beginning-of-innermost-defun)) + (python-beginning-of-defun-function)) (back-to-indentation) (looking-at "\\(?:def\\|class\\) +\\([^(]+\\)[^:]+:\\s-*\n") (setq names (cons (match-string-no-properties 1) names))))) -- cgit v1.2.1 From 6b4328531cd97745c25af9f055fd6ff385022c77 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:12 -0300 Subject: Fixed python-info-current-defun to match new navigation code. python-nav-beginning-of-defun and python-beginning-of-defun-function now support a new extra optional argument called NODECORATORS. --- lisp/progmodes/python.el | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6295dad559e..321bc5eb94a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -874,22 +874,24 @@ With numeric ARG, just insert that many colons. With (python-rx line-start (* space) defun (+ space) symbol-name) "Regular expresion matching beginning of innermost class or function.") -(defun python-nav-beginning-of-defun () +(defun python-nav-beginning-of-defun (&optional nodecorators) "Move point to beginning-of-defun. -This is the main part of`python-beginning-of-defun-function' +When NODECORATORS is non-nil decorators are not included. This +is the main part of`python-beginning-of-defun-function' implementation." (let ((indent-pos (save-excursion (back-to-indentation) (point-marker))) (include-decorators (lambda () - (when (save-excursion - (forward-line -1) - (looking-at (python-rx decorator))) - (while (and (not (bobp)) - (forward-line -1) - (looking-at (python-rx decorator)))) - (when (not (bobp)) (forward-line 1)))))) + (when (not nodecorators) + (when (save-excursion + (forward-line -1) + (looking-at (python-rx decorator))) + (while (and (not (bobp)) + (forward-line -1) + (looking-at (python-rx decorator)))) + (when (not (bobp)) (forward-line 1))))))) (if (and (> (point) indent-pos) (save-excursion (goto-char (line-beginning-position)) @@ -902,20 +904,21 @@ implementation." (goto-char (or (python-info-ppss-context 'string) (point))) (funcall include-decorators)))) -(defun python-beginning-of-defun-function (&optional arg) +(defun python-beginning-of-defun-function (&optional arg nodecorators) "Move point to the beginning of def or class. With positive ARG move that number of functions forward. With -negative do the same but backwards." +negative do the same but backwards. When NODECORATORS is non-nil +decorators are not included." (when (or (null arg) (= arg 0)) (setq arg 1)) (if (> arg 0) (dotimes (i arg) - (python-nav-beginning-of-defun)) + (python-nav-beginning-of-defun nodecorators)) (dotimes (i (- arg)) (python-end-of-defun-function) (forward-comment 1) (goto-char (line-end-position)) (when (not (eobp)) - (python-nav-beginning-of-defun))))) + (python-nav-beginning-of-defun nodecorators))))) (defun python-end-of-defun-function () "Move point to the end of def or class. @@ -1934,18 +1937,21 @@ Interactively, prompt for symbol." This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." - (let ((names '())) + (let ((names '()) + (min-indent)) (save-restriction (widen) (save-excursion - (beginning-of-line) - (when (not (>= (current-indentation) python-indent-offset)) - (while (and (not (eobp)) (forward-comment 1)))) + (forward-comment -1) + (goto-char (line-end-position)) (while (and (not (equal 0 (current-indentation))) - (python-beginning-of-defun-function)) - (back-to-indentation) - (looking-at "\\(?:def\\|class\\) +\\([^(]+\\)[^:]+:\\s-*\n") - (setq names (cons (match-string-no-properties 1) names))))) + (not (python-beginning-of-defun-function 1 t))) + (when (or (not min-indent) + (< (current-indentation) min-indent)) + (setq min-indent (current-indentation)) + (back-to-indentation) + (looking-at "\\(?:def\\|class\\) +\\([^(]+\\)[^:]+:\\s-*\n") + (setq names (cons (match-string-no-properties 1) names)))))) (when names (mapconcat (lambda (string) string) names ".")))) -- cgit v1.2.1 From 327f0e84294143404ae7889a473c978fbd235378 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:12 -0300 Subject: Small cornercase fix to python-info-current-defun. Returned the bad defun name when point was at the beginning of defun. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 321bc5eb94a..b11741d4a68 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1942,8 +1942,8 @@ not inside a defun." (save-restriction (widen) (save-excursion - (forward-comment -1) (goto-char (line-end-position)) + (forward-comment -1) (while (and (not (equal 0 (current-indentation))) (not (python-beginning-of-defun-function 1 t))) (when (or (not min-indent) -- cgit v1.2.1 From 053a6c726f8bd871724bcf45978b63926bd5f769 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:13 -0300 Subject: Docstrings enhancements --- lisp/progmodes/python.el | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b11741d4a68..0b9d99cbbee 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -875,7 +875,7 @@ With numeric ARG, just insert that many colons. With "Regular expresion matching beginning of innermost class or function.") (defun python-nav-beginning-of-defun (&optional nodecorators) - "Move point to beginning-of-defun. + "Move point to `beginning-of-defun'. When NODECORATORS is non-nil decorators are not included. This is the main part of`python-beginning-of-defun-function' implementation." @@ -1566,11 +1566,13 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (t t)))) (defun python-fill-comment (&optional justify) - "Comment fill function for `python-fill-paragraph-function'." + "Comment fill function for `python-fill-paragraph-function'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'." (fill-comment-paragraph justify)) (defun python-fill-string (&optional justify) - "String fill function for `python-fill-paragraph-function'." + "String fill function for `python-fill-paragraph-function'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'." (let ((marker (point-marker)) (string-start-marker (progn @@ -1610,11 +1612,13 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (fill-paragraph justify)))) t) (defun python-fill-decorator (&optional justify) - "Decorator fill function for `python-fill-paragraph-function'." + "Decorator fill function for `python-fill-paragraph-function'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'." t) (defun python-fill-paren (&optional justify) - "Paren fill function for `python-fill-paragraph-function'." + "Paren fill function for `python-fill-paragraph-function'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'." (save-restriction (narrow-to-region (progn (while (python-info-ppss-context 'paren) -- cgit v1.2.1 From 74d7b6051a6392f7de1ef4ef5d6b30e3f582d1b6 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:13 -0300 Subject: Fixed infinite loop on python-info-current-defun For this python-beginning-of-defun-function has been modified to return t or nil if a defun was found. --- lisp/progmodes/python.el | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0b9d99cbbee..5abfe91809b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -878,10 +878,12 @@ With numeric ARG, just insert that many colons. With "Move point to `beginning-of-defun'. When NODECORATORS is non-nil decorators are not included. This is the main part of`python-beginning-of-defun-function' -implementation." +implementation. Return non-nil if point is moved to the +beginning-of-defun." (let ((indent-pos (save-excursion (back-to-indentation) (point-marker))) + (found) (include-decorators (lambda () (when (not nodecorators) @@ -898,27 +900,32 @@ implementation." (looking-at python-nav-beginning-of-defun-regexp))) (progn (goto-char (line-beginning-position)) - (funcall include-decorators)) + (funcall include-decorators) + (setq found t)) (goto-char (line-beginning-position)) - (re-search-backward python-nav-beginning-of-defun-regexp nil t) + (when (re-search-backward python-nav-beginning-of-defun-regexp nil t) + (setq found t)) (goto-char (or (python-info-ppss-context 'string) (point))) - (funcall include-decorators)))) + (funcall include-decorators)) + found)) (defun python-beginning-of-defun-function (&optional arg nodecorators) "Move point to the beginning of def or class. With positive ARG move that number of functions forward. With negative do the same but backwards. When NODECORATORS is non-nil -decorators are not included." +decorators are not included. Return non-nil if point is moved to the +beginning-of-defun." (when (or (null arg) (= arg 0)) (setq arg 1)) (if (> arg 0) - (dotimes (i arg) - (python-nav-beginning-of-defun nodecorators)) - (dotimes (i (- arg)) - (python-end-of-defun-function) - (forward-comment 1) - (goto-char (line-end-position)) - (when (not (eobp)) - (python-nav-beginning-of-defun nodecorators))))) + (dotimes (i arg (python-nav-beginning-of-defun nodecorators))) + (let ((found)) + (dotimes (i (- arg) found) + (python-end-of-defun-function) + (forward-comment 1) + (goto-char (line-end-position)) + (when (not (eobp)) + (setq found + (python-nav-beginning-of-defun nodecorators))))))) (defun python-end-of-defun-function () "Move point to the end of def or class. @@ -1949,7 +1956,7 @@ not inside a defun." (goto-char (line-end-position)) (forward-comment -1) (while (and (not (equal 0 (current-indentation))) - (not (python-beginning-of-defun-function 1 t))) + (python-beginning-of-defun-function 1 t)) (when (or (not min-indent) (< (current-indentation) min-indent)) (setq min-indent (current-indentation)) -- cgit v1.2.1 From af5c1bebb58a9cbee25c16b96f6725d0e1fabf77 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:13 -0300 Subject: Fixed python-info-current-defun for classes without bases --- lisp/progmodes/python.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5abfe91809b..2f0393deb65 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -871,8 +871,10 @@ With numeric ARG, just insert that many colons. With ;;; Navigation (defvar python-nav-beginning-of-defun-regexp - (python-rx line-start (* space) defun (+ space) symbol-name) - "Regular expresion matching beginning of innermost class or function.") + (python-rx line-start (* space) defun (+ space) (group symbol-name)) + "Regular expresion matching beginning of class or function. +The name of the class or function should be in a group so it can +be retrieved via `match-string'.") (defun python-nav-beginning-of-defun (&optional nodecorators) "Move point to `beginning-of-defun'. @@ -1960,8 +1962,7 @@ not inside a defun." (when (or (not min-indent) (< (current-indentation) min-indent)) (setq min-indent (current-indentation)) - (back-to-indentation) - (looking-at "\\(?:def\\|class\\) +\\([^(]+\\)[^:]+:\\s-*\n") + (looking-at python-nav-beginning-of-defun-regexp) (setq names (cons (match-string-no-properties 1) names)))))) (when names (mapconcat (lambda (string) string) names ".")))) -- cgit v1.2.1 From 2d63ad564d8ca5258bb6a94fbec63403e1ff56e5 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:14 -0300 Subject: Documentation enhancements with regards to code checking. (thanks schickm) --- lisp/progmodes/python.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2f0393deb65..884a84c382a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -116,8 +116,8 @@ ;; out of the box. This feature needs an inferior python shell ;; running. -;; Code check: Check the current file for errors using -;; `python-check-command' +;; Code check: Check the current file for errors with `python-check' +;; using the program defined in `python-check-command'. ;; Eldoc: returns documentation for object at point by using the ;; inferior python subprocess to inspect its documentation. As you -- cgit v1.2.1 From fc2dc7df0209283c66e21b20c2b6bafe29d6c331 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:14 -0300 Subject: Implemented imenu support. New variables: + python-imenu-include-defun-type + python-imenu-make-tree + python-imenu-subtree-root-label + python-imenu-index-alist New Functions: + python-imenu-tree-assoc + python-imenu-make-element-tree + python-imenu-make-tree + python-imenu-create-index API changes: + python-info-current-defun now supports an optional argument called INCLUDE-TYPE. --- lisp/progmodes/python.el | 147 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 9 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 884a84c382a..a582dc6db2a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -34,7 +34,7 @@ ;; Implements Syntax highlighting, Indentation, Movement, Shell ;; interaction, Shell completion, Pdb tracking, Symbol completion, -;; Skeletons, FFAP, Code Check, Eldoc. +;; Skeletons, FFAP, Code Check, Eldoc, imenu. ;; Syntax highlighting: Fontification of code is provided and supports ;; python's triple quoted strings properly. @@ -124,6 +124,11 @@ ;; might guessed you should run `python-shell-send-buffer' from time ;; to time to get better results too. +;; imenu: This mode supports imenu. It builds a plain or tree menu +;; depending on the value of `python-imenu-make-tree'. Also you can +;; customize if menu items should include its type using +;; `python-imenu-include-defun-type'. + ;; If you used python-mode.el you probably will miss auto-indentation ;; when inserting newlines. To achieve the same behavior you have ;; two options: @@ -873,15 +878,15 @@ With numeric ARG, just insert that many colons. With (defvar python-nav-beginning-of-defun-regexp (python-rx line-start (* space) defun (+ space) (group symbol-name)) "Regular expresion matching beginning of class or function. -The name of the class or function should be in a group so it can -be retrieved via `match-string'.") +The name of the defun should be grouped so it can be retrieved +via `match-string'.") (defun python-nav-beginning-of-defun (&optional nodecorators) "Move point to `beginning-of-defun'. When NODECORATORS is non-nil decorators are not included. This is the main part of`python-beginning-of-defun-function' implementation. Return non-nil if point is moved to the -beginning-of-defun." +`beginning-of-defun'." (let ((indent-pos (save-excursion (back-to-indentation) (point-marker))) @@ -916,7 +921,7 @@ beginning-of-defun." With positive ARG move that number of functions forward. With negative do the same but backwards. When NODECORATORS is non-nil decorators are not included. Return non-nil if point is moved to the -beginning-of-defun." +`beginning-of-defun'." (when (or (null arg) (= arg 0)) (setq arg 1)) (if (> arg 0) (dotimes (i arg (python-nav-beginning-of-defun nodecorators))) @@ -1943,10 +1948,127 @@ Interactively, prompt for symbol." (help-print-return-message))))))) +;;; Imenu + +(defcustom python-imenu-include-defun-type t + "Non-nil make imenu items to include its type." + :type 'boolean + :group 'python + :safe 'booleanp) + +(defcustom python-imenu-make-tree nil + "Non-nil make imenu to build a tree menu. +Set to nil for speed." + :type 'boolean + :group 'python + :safe 'booleanp) + +(defcustom python-imenu-subtree-root-label "" + "Label displayed to navigate to root from a subtree. +It can contain a \"%s\" which will be replaced with the root name." + :type 'string + :group 'python + :safe 'stringp) + +(defvar python-imenu-index-alist nil + "Calculated index tree for imenu.") + +(defun python-imenu-tree-assoc (keylist tree) + "Using KEYLIST traverse TREE." + (if keylist + (python-imenu-tree-assoc (cdr keylist) + (ignore-errors (assoc (car keylist) tree))) + tree)) + +(defun python-imenu-make-element-tree (element-list full-element plain-index) + "Make a tree from plain alist of module names. +ELEMENT-LIST is the defun name splitted by \".\" and FULL-ELEMENT +is the same thing, the difference is that FULL-ELEMENT remains +untouched in all recursive calls. +Argument PLAIN-INDEX is the calculated plain index used to build the tree." + (when (not (python-imenu-tree-assoc full-element python-imenu-index-alist)) + (when element-list + (let* ((subelement-point (cdr (assoc + (mapconcat #'identity full-element ".") + plain-index))) + (subelement-name (car element-list)) + (subelement-position (position subelement-name full-element)) + (subelement-path (when subelement-position + (butlast + full-element + (- (length full-element) + subelement-position))))) + (let ((path-ref (python-imenu-tree-assoc subelement-path + python-imenu-index-alist))) + (if (not path-ref) + (push (cons subelement-name subelement-point) + python-imenu-index-alist) + (when (not (listp (cdr path-ref))) + ;; Modifiy root cdr to be a list + (setcdr path-ref + (list (cons (format python-imenu-subtree-root-label + (car path-ref)) + (cdr (assoc + (mapconcat #'identity + subelement-path ".") + plain-index)))))) + (when (not (assoc subelement-name path-ref)) + (push (cons subelement-name subelement-point) (cdr path-ref)))))) + (python-imenu-make-element-tree (cdr element-list) + full-element plain-index)))) + +(defun python-imenu-make-tree (index) +"Build the imenu alist tree from plain INDEX. + +The idea of this function is that given the alist: + + '((\"Test\" . 100) + (\"Test.__init__\" . 200) + (\"Test.some_method\" . 300) + (\"Test.some_method.another\" . 400) + (\"Test.something_else\" . 500) + (\"test\" . 600) + (\"test.reprint\" . 700) + (\"test.reprint\" . 800)) + +This tree gets built: + + '((\"Test\" . ((\"jump to...\" . 100) + (\"__init__\" . 200) + (\"some_method\" . ((\"jump to...\" . 300) + (\"another\" . 400))) + (\"something_else\" . 500))) + (\"test\" . ((\"jump to...\" . 600) + (\"reprint\" . 700) + (\"reprint\" . 800)))) + +Internally it uses `python-imenu-make-element-tree' to create all +branches for each element." +(setq python-imenu-index-alist nil) +(mapcar (lambda (element) + (python-imenu-make-element-tree element element index)) + (mapcar (lambda (element) + (split-string (car element) "\\." t)) index)) +python-imenu-index-alist) + +(defun python-imenu-create-index () + "`imenu-create-index-function' for Python." + (let ((index)) + (goto-char (point-max)) + (while (python-beginning-of-defun-function 1 t) + (let ((defun-dotted-name + (python-info-current-defun python-imenu-include-defun-type))) + (push (cons defun-dotted-name (point)) index))) + (if python-imenu-make-tree + (python-imenu-make-tree index) + index))) + + ;;; Misc helpers -(defun python-info-current-defun () +(defun python-info-current-defun (&optional include-type) "Return name of surrounding function with Python compatible dotty syntax. +Optional argument INCLUDE-TYPE indicates to include the type of the defun. This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." @@ -1957,13 +2079,18 @@ not inside a defun." (save-excursion (goto-char (line-end-position)) (forward-comment -1) - (while (and (not (equal 0 (current-indentation))) - (python-beginning-of-defun-function 1 t)) + (while (python-beginning-of-defun-function 1 t) (when (or (not min-indent) (< (current-indentation) min-indent)) (setq min-indent (current-indentation)) (looking-at python-nav-beginning-of-defun-regexp) - (setq names (cons (match-string-no-properties 1) names)))))) + (setq names (cons + (if (not include-type) + (match-string-no-properties 1) + (mapconcat 'identity + (split-string + (match-string-no-properties 0)) " ")) + names)))))) (when names (mapconcat (lambda (string) string) names ".")))) @@ -2103,6 +2230,8 @@ if that value is non-nil." (add-hook 'completion-at-point-functions 'python-completion-complete-at-point nil 'local) + (setq imenu-create-index-function #'python-imenu-create-index) + (set (make-local-variable 'add-log-current-defun-function) #'python-info-current-defun) -- cgit v1.2.1 From c942de99c033702db1c8d3bbc1fb847374f3b563 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:14 -0300 Subject: fixed CL related bytecompilation errors and set make-tree for imenu default --- lisp/progmodes/python.el | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a582dc6db2a..860f0859b01 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1152,13 +1152,13 @@ run). (proc-buffer-name (format "*%s*" proc-name)) (process-environment (if python-shell-process-environment - (merge 'list python-shell-process-environment - process-environment 'string=) + (python-util-merge 'list python-shell-process-environment + process-environment 'string=) process-environment)) (exec-path (if python-shell-exec-path - (merge 'list python-shell-exec-path - exec-path 'string=) + (python-util-merge 'list python-shell-exec-path + exec-path 'string=) exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let ((cmdlist (split-string-and-unquote cmd))) @@ -1956,7 +1956,7 @@ Interactively, prompt for symbol." :group 'python :safe 'booleanp) -(defcustom python-imenu-make-tree nil +(defcustom python-imenu-make-tree t "Non-nil make imenu to build a tree menu. Set to nil for speed." :type 'boolean @@ -1992,7 +1992,8 @@ Argument PLAIN-INDEX is the calculated plain index used to build the tree." (mapconcat #'identity full-element ".") plain-index))) (subelement-name (car element-list)) - (subelement-position (position subelement-name full-element)) + (subelement-position (python-util-position + subelement-name full-element)) (subelement-path (when subelement-position (butlast full-element @@ -2045,10 +2046,10 @@ This tree gets built: Internally it uses `python-imenu-make-element-tree' to create all branches for each element." (setq python-imenu-index-alist nil) -(mapcar (lambda (element) - (python-imenu-make-element-tree element element index)) - (mapcar (lambda (element) - (split-string (car element) "\\." t)) index)) +(mapc (lambda (element) + (python-imenu-make-element-tree element element index)) + (mapcar (lambda (element) + (split-string (car element) "\\." t)) index)) python-imenu-index-alist) (defun python-imenu-create-index () @@ -2195,6 +2196,29 @@ character address of the specified TYPE." (t nil)))) +;;; Utility functions + +;; Stolen from GNUS +(defun python-util-merge (type list1 list2 pred) + "Destructively merge lists LIST1 and LIST2 to produce a new list. +Argument TYPE is for compatibility and ignored. +Ordering of the elements is preserved according to PRED, a `less-than' +predicate on the elements." + (let ((res nil)) + (while (and list1 list2) + (if (funcall pred (car list2) (car list1)) + (push (pop list2) res) + (push (pop list1) res))) + (nconc (nreverse res) list1 list2))) + +(defun python-util-position (item seq) + "Find the first occurrence of ITEM in SEQ. +Return the index of the matching item, or nil if not found." + (let ((member-result (member item seq))) + (when member-result + (- (length seq) (length member-result))))) + + ;;;###autoload (define-derived-mode python-mode fundamental-mode "Python" "Major mode for editing Python files. -- cgit v1.2.1 From 3697b5314ae77a50e12972b70f8367d8d3ba7853 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:15 -0300 Subject: Added python-nav-sentence-start and python-nav-sentence-end functions --- lisp/progmodes/python.el | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 860f0859b01..aa20c20fae6 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -590,14 +590,7 @@ START is the buffer position where the sexp starts." ;; After normal line ((setq start (save-excursion (while (and (forward-comment -1) (not (bobp)))) - (while (and (not (back-to-indentation)) - (not (bobp)) - (if (python-info-ppss-context 'paren) - (forward-line -1) - (if (save-excursion - (forward-line -1) - (python-info-line-ends-backslash-p)) - (forward-line -1))))) + (python-nav-sentence-start) (point-marker))) 'after-line) ;; Do not indent @@ -955,6 +948,28 @@ Returns nil if point is not in a def or class." (forward-comment 1) (goto-char (line-beginning-position)))) +(defun python-nav-sentence-start () + "Move to start of current sentence." + (interactive "^") + (while (and (not (back-to-indentation)) + (not (bobp)) + (when (or + (save-excursion + (forward-line -1) + (python-info-line-ends-backslash-p)) + (python-info-ppss-context 'paren)) + (forward-line -1))))) + +(defun python-nav-sentence-end () + "Move to end of current sentence." + (interactive "^") + (while (and (goto-char (line-end-position)) + (not (eobp)) + (when (or + (python-info-line-ends-backslash-p) + (python-info-ppss-context 'paren)) + (forward-line 1))))) + ;;; Shell integration -- cgit v1.2.1 From 9fff18585a75ad540aedc84b1147a4a327d1dbdf Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:15 -0300 Subject: Implemented python-nav-backward-sentence, python-nav-forward-sentence Also small fixes to python-nav-sentence-start and python-nav-sentence-end were added. --- lisp/progmodes/python.el | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index aa20c20fae6..b45b22c139c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -189,6 +189,13 @@ (defvar python-mode-map (let ((map (make-sparse-keymap))) + ;; Movement + (substitute-key-definition 'backward-sentence + 'python-nav-backward-sentence + map global-map) + (substitute-key-definition 'forward-sentence + 'python-nav-forward-sentence + map global-map) ;; Indent specific (define-key map "\177" 'python-indent-dedent-line-backspace) (define-key map (kbd "") 'python-indent-dedent-line) @@ -957,6 +964,7 @@ Returns nil if point is not in a def or class." (save-excursion (forward-line -1) (python-info-line-ends-backslash-p)) + (python-info-ppss-context 'string) (python-info-ppss-context 'paren)) (forward-line -1))))) @@ -967,9 +975,35 @@ Returns nil if point is not in a def or class." (not (eobp)) (when (or (python-info-line-ends-backslash-p) + (python-info-ppss-context 'string) (python-info-ppss-context 'paren)) (forward-line 1))))) +(defun python-nav-backward-sentence (&optional arg) + "Move backward to start of sentence. With arg, do it arg times. +See `python-nav-forward-sentence' for more information." + (interactive "^p") + (or arg (setq arg 1)) + (python-nav-forward-sentence (- arg))) + +(defun python-nav-forward-sentence (&optional arg) + "Move forward to next end of sentence. With argument, repeat. +With negative argument, move backward repeatedly to start of sentence." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (forward-comment 9999) + (python-nav-sentence-end) + (forward-line 1) + (setq arg (1- arg))) + (while (< arg 0) + (python-nav-sentence-end) + (forward-comment -9999) + (python-nav-sentence-start) + (forward-line -1) + (setq arg (1+ arg)))) + + ;;; Shell integration -- cgit v1.2.1 From 107c2439fa1e48afebe20ed96fc5998011e07484 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:16 -0300 Subject: indentation fixes on after backslash --- lisp/progmodes/python.el | 68 ++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 28 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b45b22c139c..075aac4c0f5 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -644,33 +644,46 @@ START is the buffer position where the sexp starts." (back-to-indentation) (when (looking-at "\\.") (forward-line -1) - (back-to-indentation) - (forward-char (length - (with-syntax-table python-dotty-syntax-table - (current-word)))) - (re-search-backward "\\." (line-beginning-position) t 1) - (current-column)))) - (indentation (cond (block-continuation - (goto-char block-continuation) - (re-search-forward - (python-rx block-start (* space)) - (line-end-position) t) - (current-column)) - (assignment-continuation - (goto-char assignment-continuation) - (re-search-forward - (python-rx simple-operator) - (line-end-position) t) - (forward-char 1) - (re-search-forward - (python-rx (* space)) - (line-end-position) t) - (current-column)) - (dot-continuation - dot-continuation) - (t - (goto-char context-start) - (current-indentation))))) + (goto-char (line-end-position)) + (while (and (re-search-backward "\\." (line-beginning-position) t) + (or (python-info-ppss-context 'comment) + (python-info-ppss-context 'string) + (python-info-ppss-context 'paren)))) + (if (and (looking-at "\\.") + (not (or (python-info-ppss-context 'comment) + (python-info-ppss-context 'string) + (python-info-ppss-context 'paren)))) + (current-column) + (+ (current-indentation) python-indent-offset))))) + (indentation (cond + (dot-continuation + dot-continuation) + (block-continuation + (goto-char block-continuation) + (re-search-forward + (python-rx block-start (* space)) + (line-end-position) t) + (current-column)) + (assignment-continuation + (goto-char assignment-continuation) + (re-search-forward + (python-rx simple-operator) + (line-end-position) t) + (forward-char 1) + (re-search-forward + (python-rx (* space)) + (line-end-position) t) + (current-column)) + (t + (goto-char context-start) + (if (not (member + (save-excursion + (back-to-indentation) + (message (current-word))) + '("return" "import" "from"))) + (current-indentation) + (+ (current-indentation) + python-indent-offset)))))) indentation)) ('inside-paren (or (save-excursion @@ -1003,7 +1016,6 @@ With negative argument, move backward repeatedly to start of sentence." (forward-line -1) (setq arg (1+ arg)))) - ;;; Shell integration -- cgit v1.2.1 From 4cafacb52a62b81ea1396e540ead833c81ac20a4 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:16 -0300 Subject: docstring enhancements --- lisp/progmodes/python.el | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 075aac4c0f5..66da1a1388b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -46,7 +46,12 @@ ;; causes the current line to be dedented automatically if needed. ;; Movement: `beginning-of-defun' and `end-of-defun' functions are -;; properly implemented. +;; properly implemented. Also there are specialized +;; `forward-sentence' and `backward-sentence' replacements +;; (`python-nav-forward-sentence', `python-nav-backward-sentence' +;; respectively). Extra functions `python-nav-sentence-start' and +;; `python-nav-sentence-end' are included to move to the beginning and +;; to the end of a setence while taking care of multiline definitions. ;; Shell interaction: is provided and allows you easily execute any ;; block of code of your current buffer in an inferior Python process. @@ -57,7 +62,7 @@ ;; IPython) it should be easy to integrate another way to calculate ;; completions. You just need to specify your custom ;; `python-shell-completion-setup-code' and -;; `python-shell-completion-string-code' +;; `python-shell-completion-string-code'. ;; Here is a complete example of the settings you would use for ;; iPython @@ -124,8 +129,8 @@ ;; might guessed you should run `python-shell-send-buffer' from time ;; to time to get better results too. -;; imenu: This mode supports imenu. It builds a plain or tree menu -;; depending on the value of `python-imenu-make-tree'. Also you can +;; imenu: This mode supports imenu. It builds a plain or tree menu +;; depending on the value of `python-imenu-make-tree'. Also you can ;; customize if menu items should include its type using ;; `python-imenu-include-defun-type'. @@ -291,7 +296,7 @@ "Additional Python specific sexps for `python-rx'")) (defmacro python-rx (&rest regexps) - "Python mode especialized rx macro which supports common python named REGEXPS." + "Python mode specialized rx macro which supports common python named REGEXPS." (let ((rx-constituents (append python-rx-constituents rx-constituents))) (cond ((null regexps) (error "No regexp")) @@ -993,14 +998,14 @@ Returns nil if point is not in a def or class." (forward-line 1))))) (defun python-nav-backward-sentence (&optional arg) - "Move backward to start of sentence. With arg, do it arg times. + "Move backward to start of sentence. With ARG, do it arg times. See `python-nav-forward-sentence' for more information." (interactive "^p") (or arg (setq arg 1)) (python-nav-forward-sentence (- arg))) (defun python-nav-forward-sentence (&optional arg) - "Move forward to next end of sentence. With argument, repeat. + "Move forward to next end of sentence. With ARG, repeat. With negative argument, move backward repeatedly to start of sentence." (interactive "^p") (or arg (setq arg 1)) @@ -2261,10 +2266,10 @@ character address of the specified TYPE." ;; Stolen from GNUS (defun python-util-merge (type list1 list2 pred) - "Destructively merge lists LIST1 and LIST2 to produce a new list. -Argument TYPE is for compatibility and ignored. -Ordering of the elements is preserved according to PRED, a `less-than' -predicate on the elements." + "Destructively merge lists to produce a new one. +Argument TYPE is for compatibility and ignored. LIST1 and LIST2 +are the list to be merged. Ordering of the elements is preserved +according to PRED, a `less-than' predicate on the elements." (let ((res nil)) (while (and list1 list2) (if (funcall pred (car list2) (car list1)) -- cgit v1.2.1 From 589cefd7664e38a242d795ea8254bb51c6246f29 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:16 -0300 Subject: increased forward-comment COUNT variable value in all calls --- lisp/progmodes/python.el | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 66da1a1388b..24fb37259b3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -519,7 +519,7 @@ These make `python-indent-calculate-indentation' subtract the value of (not (python-info-ppss-context 'comment)) (progn (goto-char (line-end-position)) - (forward-comment -1) + (forward-comment -9999) (eq ?: (char-before)))) (setq found-block t))) (if (not found-block) @@ -531,7 +531,7 @@ These make `python-indent-calculate-indentation' subtract the value of (not (eobp))) (forward-line 1)) (forward-line 1) - (forward-comment 1) + (forward-comment 9999) (let ((indent-offset (current-indentation))) (when (> indent-offset 0) (setq python-indent-offset indent-offset)))))))) @@ -578,7 +578,7 @@ START is the buffer position where the sexp starts." (let ((block-regexp (python-rx block-start)) (block-start-line-end ":[[:space:]]*$")) (back-to-indentation) - (while (and (forward-comment -1) (not (bobp)))) + (while (and (forward-comment -9999) (not (bobp)))) (back-to-indentation) (when (or (python-info-continuation-line-p) (and (not (looking-at block-regexp)) @@ -601,7 +601,7 @@ START is the buffer position where the sexp starts." 'after-beginning-of-block) ;; After normal line ((setq start (save-excursion - (while (and (forward-comment -1) (not (bobp)))) + (while (and (forward-comment -9999) (not (bobp)))) (python-nav-sentence-start) (point-marker))) 'after-line) @@ -692,7 +692,7 @@ START is the buffer position where the sexp starts." indentation)) ('inside-paren (or (save-excursion - (forward-comment 1) + (forward-comment 9999) (when (and (looking-at (regexp-opt '(")" "]" "}"))) (not (forward-char 1)) (not (python-info-ppss-context 'paren))) @@ -707,10 +707,10 @@ START is the buffer position where the sexp starts." (narrow-to-region (line-beginning-position) (line-end-position)) - (forward-comment 1)) + (forward-comment 9999)) (if (looking-at "$") (+ (current-indentation) python-indent-offset) - (forward-comment 1) + (forward-comment 9999) (current-column))) (if (progn (back-to-indentation) @@ -946,7 +946,7 @@ decorators are not included. Return non-nil if point is moved to the (let ((found)) (dotimes (i (- arg) found) (python-end-of-defun-function) - (forward-comment 1) + (forward-comment 9999) (goto-char (line-end-position)) (when (not (eobp)) (setq found @@ -970,7 +970,7 @@ Returns nil if point is not in a def or class." (not (eobp)) (or (not (current-word)) (> (current-indentation) beg-defun-indent)))) - (forward-comment 1) + (forward-comment 9999) (goto-char (line-beginning-position)))) (defun python-nav-sentence-start () @@ -1308,7 +1308,7 @@ the output." (with-temp-buffer (insert output-buffer) (goto-char (point-min)) - (forward-comment 1) + (forward-comment 9999) (buffer-substring-no-properties (or (and (looking-at python-shell-prompt-output-regexp) @@ -2145,7 +2145,7 @@ not inside a defun." (widen) (save-excursion (goto-char (line-end-position)) - (forward-comment -1) + (forward-comment -9999) (while (python-beginning-of-defun-function 1 t) (when (or (not min-indent) (< (current-indentation) min-indent)) -- cgit v1.2.1 From 65e4f7642eb551e6cfb8757cba84246a33b19590 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:17 -0300 Subject: Fixed weird cornercase behavior in python-indent-calculate-indentation. Doing (setq python-indent-levels '(0)) was causing the value of python-indent-levels to not be initialized correctly on next calls to python-indent-calculate-indentation. Using (setq python-indent-levels (list 0)) instead does the trick but I'm not sure why. --- lisp/progmodes/python.el | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 24fb37259b3..1cd1396f24f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -723,14 +723,11 @@ START is the buffer position where the sexp starts." (let* ((indentation (python-indent-calculate-indentation)) (remainder (% indentation python-indent-offset)) (steps (/ (- indentation remainder) python-indent-offset))) - (setq python-indent-levels '(0)) + (setq python-indent-levels (list 0)) (dotimes (step steps) - (setq python-indent-levels - (cons (* python-indent-offset (1+ step)) python-indent-levels))) + (push (* python-indent-offset (1+ step)) python-indent-levels)) (when (not (eq 0 remainder)) - (setq python-indent-levels - (cons (+ (* python-indent-offset steps) remainder) - python-indent-levels))) + (push (+ (* python-indent-offset steps) remainder) python-indent-levels)) (setq python-indent-levels (nreverse python-indent-levels)) (setq python-indent-current-level (1- (length python-indent-levels))))) -- cgit v1.2.1 From f8994527d4968182a2d111be6c5e5d4a93fc5eb3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:17 -0300 Subject: fixed last paren indentation logic --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1cd1396f24f..4589a828ebe 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -692,7 +692,7 @@ START is the buffer position where the sexp starts." indentation)) ('inside-paren (or (save-excursion - (forward-comment 9999) + (skip-syntax-forward "\s" (line-end-position)) (when (and (looking-at (regexp-opt '(")" "]" "}"))) (not (forward-char 1)) (not (python-info-ppss-context 'paren))) -- cgit v1.2.1 From 9787f82961e029a8a096aa2a4a327eebb974b13c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:18 -0300 Subject: Indentation enhancements on after-backslash Multiline sentences beginning with "import", "from" or "return" are indented correctly now. --- lisp/progmodes/python.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4589a828ebe..20176944ebc 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -681,14 +681,15 @@ START is the buffer position where the sexp starts." (current-column)) (t (goto-char context-start) - (if (not (member - (save-excursion - (back-to-indentation) - (message (current-word))) - '("return" "import" "from"))) + (if (not + (save-excursion + (back-to-indentation) + (looking-at + "\\(?:return\\|from\\|import\\)\s+"))) (current-indentation) (+ (current-indentation) - python-indent-offset)))))) + (length + (match-string-no-properties 0)))))))) indentation)) ('inside-paren (or (save-excursion -- cgit v1.2.1 From 15cc40b8199735e600ddf718b936917ff4d79db0 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:18 -0300 Subject: Fixed eldoc behavior. * python-eldoc-setup-code: The code to get help now uses the inspect element. When an object doesn't have documentation and if it is callable it returns the signature for it. Also when an object does contain documentation it only returns the first line. * python-eldoc-at-point: has been simplified to just message the doc header of objects. * python-info-current-defun: was not taking into account the current indentation so point was always inside a defun, even if the indentation was less or equal than the defun above. --- lisp/progmodes/python.el | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 20176944ebc..c35b4eec320 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1924,10 +1924,28 @@ Runs COMMAND, a shell command, as if by `compile'. See (defvar python-eldoc-setup-code "def __PYDOC_get_help(obj): try: - import pydoc + import inspect if hasattr(obj, 'startswith'): obj = eval(obj, globals()) - doc = pydoc.getdoc(obj) + doc = inspect.getdoc(obj) + if not doc and callable(obj): + target = None + if inspect.isclass(obj) and hasattr(obj, '__init__'): + target = obj.__init__ + objtype = 'class' + else: + target = obj + objtype = 'def' + if target: + args = inspect.formatargspec( + *inspect.getargspec(target) + ) + name = obj.__name__ + doc = '{objtype} {name}{args}'.format( + objtype=objtype, name=name, args=args + ) + else: + doc = doc.splitlines()[0] except: doc = '' try: @@ -2000,16 +2018,7 @@ Interactively, prompt for symbol." (let ((process (python-shell-get-process))) (if (not process) (message "Eldoc needs an inferior Python process running.") - (let ((temp-buffer-show-hook - (lambda () - (toggle-read-only 1) - (setq view-return-to-alist - (list (cons (selected-window) help-return-method)))))) - (with-output-to-temp-buffer (help-buffer) - (with-current-buffer standard-output - (insert - (python-eldoc--get-doc-at-point symbol process)) - (help-print-return-message))))))) + (message (python-eldoc--get-doc-at-point symbol process))))) ;;; Imenu @@ -2144,9 +2153,9 @@ not inside a defun." (save-excursion (goto-char (line-end-position)) (forward-comment -9999) + (setq min-indent (current-indentation)) (while (python-beginning-of-defun-function 1 t) - (when (or (not min-indent) - (< (current-indentation) min-indent)) + (when (< (current-indentation) min-indent) (setq min-indent (current-indentation)) (looking-at python-nav-beginning-of-defun-regexp) (setq names (cons -- cgit v1.2.1 From b7f1355986c21123901e2b8039d2a85dca3e0673 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:18 -0300 Subject: Added package information for ELPA conformance --- lisp/progmodes/python.el | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c35b4eec320..5262fa160f9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3,6 +3,8 @@ ;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. ;; Author: FabiĆ”n E. Gallina +;; URL: https://github.com/fgallina/python.el +;; Version: 0.23.1 ;; Maintainer: FSF ;; Created: Jul 2010 ;; Keywords: languages -- cgit v1.2.1 From 0b7b2e512dcad0b81d284414856bb9cb5da5f434 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:19 -0300 Subject: python-info-current-defun: fixed cornercase that caused imenu to break --- lisp/progmodes/python.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5262fa160f9..949c112ee75 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2149,7 +2149,8 @@ This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." (let ((names '()) - (min-indent)) + (min-indent) + (first-run t)) (save-restriction (widen) (save-excursion @@ -2157,7 +2158,9 @@ not inside a defun." (forward-comment -9999) (setq min-indent (current-indentation)) (while (python-beginning-of-defun-function 1 t) - (when (< (current-indentation) min-indent) + (when (or (< (current-indentation) min-indent) + first-run) + (setq first-run nil) (setq min-indent (current-indentation)) (looking-at python-nav-beginning-of-defun-regexp) (setq names (cons -- cgit v1.2.1 From c61d750ea726051dce14e4427f4f7b54b4704e63 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:19 -0300 Subject: None, True, False, Ellipsis, __debug__, and NotImplemented now use font-lock-constant-face Also copyright, license, credits, quit, exit are removed from font-lock since they are added by the site module and only useful for shell interaction and not programs. See: http://docs.python.org/release/3.1.3/library/constants.html#constants-added-by-the-site-module --- lisp/progmodes/python.el | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 949c112ee75..00a6b804e72 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -325,8 +325,12 @@ (,(rx symbol-start "class" (1+ space) (group (1+ (or word ?_)))) (1 font-lock-type-face)) ;; Constants - (,(rx symbol-start (group "None" symbol-end)) - (1 font-lock-constant-face)) + (,(rx symbol-start + ;; copyright, license, credits, quit, exit are added by the + ;; site module and since they are not intended to be used in + ;; programs they are not added here either. + (or "None" "True" "False" "Ellipsis" "__debug__" "NotImplemented") + symbol-end) . font-lock-constant-face) ;; Decorators. (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) (0+ "." (1+ (or word ?_))))) @@ -339,7 +343,7 @@ "FutureWarning" "GeneratorExit" "IOError" "ImportError" "ImportWarning" "IndentationError" "IndexError" "KeyError" "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" - "NotImplemented" "NotImplementedError" "OSError" "OverflowError" + "NotImplementedError" "OSError" "OverflowError" "PendingDeprecationWarning" "ReferenceError" "RuntimeError" "RuntimeWarning" "StandardError" "StopIteration" "SyntaxError" "SyntaxWarning" "SystemError" "SystemExit" "TabError" "TypeError" @@ -350,19 +354,18 @@ ;; Builtins (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start (group - (or "_" "__debug__" "__doc__" "__import__" "__name__" "__package__" - "abs" "all" "any" "apply" "basestring" "bin" "bool" "buffer" - "bytearray" "bytes" "callable" "chr" "classmethod" "cmp" "coerce" - "compile" "complex" "copyright" "credits" "delattr" "dict" "dir" - "divmod" "enumerate" "eval" "execfile" "exit" "file" "filter" - "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" - "help" "hex" "id" "input" "int" "intern" "isinstance" "issubclass" - "iter" "len" "license" "list" "locals" "long" "map" "max" "min" - "next" "object" "oct" "open" "ord" "pow" "print" "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" - "True" "False" "Ellipsis")) symbol-end) + (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" + "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" + "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" + "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" + "execfile" "file" "filter" "float" "format" "frozenset" + "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" + "int" "intern" "isinstance" "issubclass" "iter" "len" "list" + "locals" "long" "map" "max" "min" "next" "object" "oct" "open" + "ord" "pow" "print" "property" "range" "raw_input" "reduce" + "reload" "repr" "reversed" "round" "set" "setattr" "slice" + "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "unichr" "unicode" "vars" "xrange" "zip")) symbol-end) (1 font-lock-builtin-face)) ;; asignations ;; support for a = b = c = 5 -- cgit v1.2.1 From 82c2b0de6a2c15fbe4369739b8cd72020fdc3399 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:19 -0300 Subject: Set local variable `mode-require-final-newline' to t --- lisp/progmodes/python.el | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 00a6b804e72..92cdd35d90a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2354,6 +2354,8 @@ if that value is non-nil." ,(lambda (arg) (python-end-of-defun-function)) nil)) + (set (make-local-variable 'mode-require-final-newline) t) + (set (make-local-variable 'outline-regexp) (python-rx (* space) block-start)) (set (make-local-variable 'outline-heading-end-regexp) ":\\s-*\n") -- cgit v1.2.1 From 9438f1ef3a35d6992a21cdd765c416f73a87ddab Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:20 -0300 Subject: fixed font-lock-builtin-face regexp --- lisp/progmodes/python.el | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 92cdd35d90a..851c7fe28c0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -352,21 +352,20 @@ "UserWarning" "ValueError" "Warning" "ZeroDivisionError") symbol-end) . font-lock-type-face) ;; Builtins - (,(rx (or line-start (not (any ". \t"))) (* (any " \t")) symbol-start - (group - (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" - "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" - "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" - "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" - "execfile" "file" "filter" "float" "format" "frozenset" - "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" - "int" "intern" "isinstance" "issubclass" "iter" "len" "list" - "locals" "long" "map" "max" "min" "next" "object" "oct" "open" - "ord" "pow" "print" "property" "range" "raw_input" "reduce" - "reload" "repr" "reversed" "round" "set" "setattr" "slice" - "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" - "unichr" "unicode" "vars" "xrange" "zip")) symbol-end) - (1 font-lock-builtin-face)) + (,(rx symbol-start + (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" + "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" + "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" + "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" + "execfile" "file" "filter" "float" "format" "frozenset" + "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" + "int" "intern" "isinstance" "issubclass" "iter" "len" "list" + "locals" "long" "map" "max" "min" "next" "object" "oct" "open" + "ord" "pow" "print" "property" "range" "raw_input" "reduce" + "reload" "repr" "reversed" "round" "set" "setattr" "slice" + "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "unichr" "unicode" "vars" "xrange" "zip") + symbol-end) . font-lock-builtin-face) ;; asignations ;; support for a = b = c = 5 (,(lambda (limit) -- cgit v1.2.1 From f2a972f356997bbeeb1a4b22f305749dc61bbe03 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:20 -0300 Subject: Updated TODO --- lisp/progmodes/python.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 851c7fe28c0..33b92b0129a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -162,9 +162,8 @@ ;; Ordered by priority: -;; Better decorator support for beginning of defun - -;; Review code and cleanup +;; Give a better interface for virtualenv support in interactive +;; shells ;;; Code: -- cgit v1.2.1 From 33d0aec16e93b6993283c0d4b46f21a1600f9e8f Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:20 -0300 Subject: Make package description separator contain 3 dashes (ELPA conformance) --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 33b92b0129a..51f0cfa80eb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,4 +1,4 @@ -;;; python.el -- Python's flying circus support for Emacs +;;; python.el --- Python's flying circus support for Emacs ;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. -- cgit v1.2.1 From ecf24fd762c38e0a588198bc702b2f5866957f86 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:21 -0300 Subject: Backported triple quote syntax from Emacs 24 --- lisp/progmodes/python.el | 66 +++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 40 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 51f0cfa80eb..247deec6d3e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -395,26 +395,15 @@ (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)))) -;; Fixme: Is there a better way? (defconst python-font-lock-syntactic-keywords + ;; Make outer chars of matching triple-quote sequences into generic + ;; string delimiters. Fixme: Is there a better way? ;; First avoid a sequence preceded by an odd number of backslashes. - `((,(rx (not (any ?\\)) - ?\\ (* (and ?\\ ?\\)) - (group (syntax string-quote)) - (backref 1) - (group (backref 1))) - (2 ,(string-to-syntax "\""))) ; dummy - (,(rx (group (optional (any "uUrR"))) ; prefix gets syntax property - (optional (any "rR")) ; possible second prefix - (group (syntax string-quote)) ; maybe gets property - (backref 2) ; per first quote - (group (backref 2))) ; maybe gets property - (1 (python-quote-syntax 1)) - (2 (python-quote-syntax 2)) - (3 (python-quote-syntax 3)))) - "Make outer chars of triple-quote strings into generic string delimiters.") - -(defun python-quote-syntax (n) + `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") + (3 (python-quote-syntax))))) + +(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 @@ -432,28 +421,25 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)." ;; x '"""' x """ \"""" x (save-excursion (goto-char (match-beginning 0)) - (cond - ;; Consider property for the last char if in a fenced string. - ((= n 3) - (let* ((font-lock-syntactic-keywords nil) - (syntax (syntax-ppss))) - (when (eq t (nth 3 syntax)) ; after unclosed fence - (goto-char (nth 8 syntax)) ; fence position - (skip-chars-forward "uUrR") ; skip any prefix - ;; Is it a matching sequence? - (if (eq (char-after) (char-after (match-beginning 2))) - (eval-when-compile (string-to-syntax "|")))))) - ;; Consider property for initial char, accounting for prefixes. - ((or (and (= n 2) ; leading quote (not prefix) - (= (match-beginning 1) (match-end 1))) ; prefix is null - (and (= n 1) ; prefix - (/= (match-beginning 1) (match-end 1)))) ; non-empty - (let ((font-lock-syntactic-keywords nil)) - (unless (eq 'string (syntax-ppss-context (syntax-ppss))) - (eval-when-compile (string-to-syntax "|"))))) - ;; Otherwise (we're in a non-matching string) the property is - ;; nil, which is OK. - ))) + (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 "|")))) + ))) (defvar python-mode-syntax-table (let ((table (make-syntax-table))) -- cgit v1.2.1 From 1fe1b5aa848d02ac3b1706ab3c332b8578567c54 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:21 -0300 Subject: Implemented internal python shell. This new kind of shell is intended to be used for generic communication related to defined configurations. The main difference with global or dedicated shells is that these ones are attached to a configuration, not a buffer. This means that can be used for example to retrieve the sys.path and other stuff, without messing with user shells. New Variables: * python-shell-internal-buffer-name, New functions: * python-shell-internal-get-process-name * run-python-internal * python-shell-internal-get-or-create-process * python-shell-internal-send-string (makes python-send-receive obsolete) --- lisp/progmodes/python.el | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 247deec6d3e..98f37fc2ac4 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1020,6 +1020,9 @@ With negative argument, move backward repeatedly to start of sentence." :group 'python :safe 'stringp) +(defvar python-shell-internal-buffer-name "Python Internal" + "Default buffer name for the Internal Python interpreter.") + (defcustom python-shell-interpreter-args "-i" "Default arguments for the Python interpreter." :type 'string @@ -1126,6 +1129,20 @@ in the `same-window-buffer-names' list." (format "*%s*" process-name))) process-name)) +(defun python-shell-internal-get-process-name () + "Calculate the appropiate process name for Internal Python process. +The name is calculated from `python-shell-global-buffer-name' and +a hash of all relevant global shell settings in order to ensure +uniqueness for different types of configurations." + (format "%s [%s]" + python-shell-internal-buffer-name + (md5 + (concat + (python-shell-parse-command) + (mapconcat #'symbol-value python-shell-setup-codes "") + (mapconcat #'indentity python-shell-process-environment "") + (mapconcat #'indentity python-shell-exec-path ""))))) + (defun python-shell-parse-command () "Calculate the string used to execute the inferior Python process." (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) @@ -1222,6 +1239,42 @@ run). (pop-to-buffer proc-buffer-name)) dedicated) +(defun run-python-internal () + "Run an inferior Internal Python process. +Input and output via buffer named after +`python-shell-internal-buffer-name' and what +`python-shell-internal-get-process-name' returns. This new kind +of shell is intended to be used for generic communication related +to defined configurations. The main difference with global or +dedicated shells is that these ones are attached to a +configuration, not a buffer. This means that can be used for +example to retrieve the sys.path and other stuff, without messing +with user shells. Runs the hook +`inferior-python-mode-hook' (after the `comint-mode-hook' is +run). \(Type \\[describe-mode] in the process buffer for a list +of commands.)" + (interactive) + (save-excursion + (let* ((cmd (python-shell-parse-command)) + (proc-name (python-shell-internal-get-process-name)) + (proc-buffer-name (format "*%s*" proc-name)) + (process-environment + (if python-shell-process-environment + (python-util-merge 'list python-shell-process-environment + process-environment 'string=) + process-environment)) + (exec-path + (if python-shell-exec-path + (python-util-merge 'list python-shell-exec-path + exec-path 'string=) + exec-path))) + (when (not (comint-check-proc proc-buffer-name)) + (let ((cmdlist (split-string-and-unquote cmd))) + (set-buffer + (apply 'make-comint proc-name (car cmdlist) nil + (cdr cmdlist))) + (inferior-python-mode)))))) + (defun python-shell-get-process () "Get inferior Python process for current buffer and return it." (let* ((dedicated-proc-name (python-shell-get-process-name t)) @@ -1254,6 +1307,13 @@ run). dedicated-proc-buffer-name global-proc-buffer-name)))) +(defun python-shell-internal-get-or-create-process () + "Get or create an inferior Internal Python process." + (let* ((proc-name (python-shell-internal-get-process-name)) + (proc-buffer-name (format "*%s*" proc-name))) + (run-python-internal) + (get-buffer-process proc-buffer-name))) + (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. When MSG is non-nil messages the first line of STRING." @@ -1307,6 +1367,21 @@ the output." (lambda (string) string) (butlast (split-string output-buffer "\n")) "\n"))) +(defun python-shell-internal-send-string (string) + "Send STRING to the Internal Python interpreter. +Returns the output. See `python-shell-send-string-no-output'." + (python-shell-send-string-no-output + ;; Makes this function compatible with the old + ;; python-send-receive. (At least for CEDET). + (replace-regexp-in-string "_emacs_out +" "" string) + (python-shell-internal-get-or-create-process) nil)) + +(define-obsolete-function-alias + 'python-send-receive 'python-shell-internal-send-string "23.3" + "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.") + (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." (interactive "r") -- cgit v1.2.1 From 64348c3241dfbf2c6d29e1a3533ec3543c33c780 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:21 -0300 Subject: Implemented simple virtualenv setup via python-shell-virtualenv-path variable. when set to a string, makes the values stored in `python-shell-process-environment' and `python-shell-exec-path' to be modified properly so shells are started with the specified virtualenv. New Variables: * python-shell-virtualenv-path New functions: * python-shell-calculate-process-environment * python-shell-calculate-exec-path --- lisp/progmodes/python.el | 89 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 30 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 98f37fc2ac4..7284e7a1ed9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -35,8 +35,9 @@ ;; keeping it simple :) ;; Implements Syntax highlighting, Indentation, Movement, Shell -;; interaction, Shell completion, Pdb tracking, Symbol completion, -;; Skeletons, FFAP, Code Check, Eldoc, imenu. +;; interaction, Shell completion, Shell virtualenv support, Pdb +;; tracking, Symbol completion, Skeletons, FFAP, Code Check, Eldoc, +;; imenu. ;; Syntax highlighting: Fontification of code is provided and supports ;; python's triple quoted strings properly. @@ -84,12 +85,12 @@ ;; pyreadline from http://ipython.scipy.org/moin/PyReadline/Intro and ;; you should be good to go. -;; The shell also contains support for virtualenvs and other special -;; environment modification thanks to +;; Shell virtualenv support: The shell also contains support for +;; virtualenvs and other special environment modifications thanks to ;; `python-shell-process-environment' and `python-shell-exec-path'. ;; These two variables allows you to modify execution paths and ;; enviroment variables to make easy for you to setup virtualenv rules -;; or behaviors modifications when running shells. Here is an example +;; or behavior modifications when running shells. Here is an example ;; of how to make shell processes to be run using the /path/to/env/ ;; virtualenv: @@ -104,6 +105,15 @@ ;; "VIRTUAL_ENV=/path/to/env/")) ;; (python-shell-exec-path . ("/path/to/env/bin/")) +;; Since the above is cumbersome and can be programatically +;; calculated, the variable `python-shell-virtualenv-path' is +;; provided. When this variable is set with the path of the +;; virtualenv to use, `process-environment' and `exec-path' get proper +;; values in order to run shells inside the specified virtualenv. So +;; the following will achieve the same as the previous example: + +;; (setq python-shell-virtualenv-path "/path/to/env/") + ;; Pdb tracking: when you execute a block of code that contains some ;; call to pdb (or ipdb) it will prompt the block of code and will ;; follow the execution of pdb marking the current line with an arrow. @@ -160,11 +170,6 @@ ;;; TODO: -;; Ordered by priority: - -;; Give a better interface for virtualenv support in interactive -;; shells - ;;; Code: (require 'ansi-color) @@ -1085,6 +1090,16 @@ default `exec-path'." :group 'python :safe 'listp) +(defcustom python-shell-virtualenv-path nil + "Path to virtualenv root. +This variable, when set to a string, makes the values stored in +`python-shell-process-environment' and `python-shell-exec-path' +to be modified properly so shells are started with the specified +virtualenv." + :type 'string + :group 'python + :safe 'stringp) + (defcustom python-shell-setup-codes '(python-shell-completion-setup-code python-ffap-setup-code python-eldoc-setup-code) @@ -1141,12 +1156,42 @@ uniqueness for different types of configurations." (python-shell-parse-command) (mapconcat #'symbol-value python-shell-setup-codes "") (mapconcat #'indentity python-shell-process-environment "") + (or python-shell-virtualenv-path "") (mapconcat #'indentity python-shell-exec-path ""))))) (defun python-shell-parse-command () "Calculate the string used to execute the inferior Python process." (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) +(defun python-shell-calculate-process-enviroment () + "Calculate process enviroment given `python-shell-virtualenv-path'." + (let ((env (python-util-merge 'list python-shell-process-environment + process-environment 'string=)) + (virtualenv (if python-shell-virtualenv-path + (directory-file-name python-shell-virtualenv-path) + nil))) + (if (not virtualenv) + env + (dolist (envvar env) + (let* ((split (split-string envvar "=" t)) + (name (nth 0 split)) + (value (nth 1 split))) + (when (not (string= name "PYTHONHOME")) + (when (string= name "PATH") + (setq value (format "%s/bin:%s" virtualenv value))) + (setq env (cons (format "%s=%s" name value) env))))) + (cons (format "VIRTUAL_ENV=%s" virtualenv) env)))) + +(defun python-shell-calculate-exec-path () + "Calculate exec path given `python-shell-virtualenv-path'." + (let ((path (python-util-merge 'list python-shell-exec-path + exec-path 'string=))) + (if (not python-shell-virtualenv-path) + path + (cons (format "%s/bin" + (directory-file-name python-shell-virtualenv-path)) + path)))) + (defun python-comint-output-filter-function (output) "Hook run after content is put into comint buffer. OUTPUT is a string with the contents of the buffer." @@ -1220,16 +1265,8 @@ run). (list nil (python-shell-parse-command)))) (let* ((proc-name (python-shell-get-process-name dedicated)) (proc-buffer-name (format "*%s*" proc-name)) - (process-environment - (if python-shell-process-environment - (python-util-merge 'list python-shell-process-environment - process-environment 'string=) - process-environment)) - (exec-path - (if python-shell-exec-path - (python-util-merge 'list python-shell-exec-path - exec-path 'string=) - exec-path))) + (process-environment (python-shell-calculate-process-enviroment)) + (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let ((cmdlist (split-string-and-unquote cmd))) (set-buffer @@ -1258,16 +1295,8 @@ of commands.)" (let* ((cmd (python-shell-parse-command)) (proc-name (python-shell-internal-get-process-name)) (proc-buffer-name (format "*%s*" proc-name)) - (process-environment - (if python-shell-process-environment - (python-util-merge 'list python-shell-process-environment - process-environment 'string=) - process-environment)) - (exec-path - (if python-shell-exec-path - (python-util-merge 'list python-shell-exec-path - exec-path 'string=) - exec-path))) + (process-environment (python-shell-calculate-process-enviroment)) + (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let ((cmdlist (split-string-and-unquote cmd))) (set-buffer -- cgit v1.2.1 From 856552879434f16abb7bd8a7af0be34c6589264b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:22 -0300 Subject: Make python-info-continuation-line-p to check context type matches In order for a line to be continuation of another, they must be on the same context. New Function: * python-info-ppss-context-type --- lisp/progmodes/python.el | 55 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 7284e7a1ed9..cdf8419f288 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2302,22 +2302,29 @@ not inside a defun." (defun python-info-continuation-line-p () "Return non-nil if current line is continuation of another." - (or (python-info-line-ends-backslash-p) - (string-match ",[[:space:]]*$" (buffer-substring - (line-beginning-position) - (line-end-position))) - (save-excursion - (let ((innermost-paren (progn - (goto-char (line-end-position)) - (python-info-ppss-context 'paren)))) - (when (and innermost-paren - (and (<= (line-beginning-position) innermost-paren) - (>= (line-end-position) innermost-paren))) - (goto-char innermost-paren) - (looking-at (python-rx open-paren (* space) line-end))))) - (save-excursion - (back-to-indentation) - (python-info-ppss-context 'paren)))) + (let ((current-ppss-context-type (python-info-ppss-context-type))) + (and + (equal (save-excursion + (goto-char (line-end-position)) + (forward-comment 9999) + (python-info-ppss-context-type)) + current-ppss-context-type) + (or (python-info-line-ends-backslash-p) + (string-match ",[[:space:]]*$" (buffer-substring + (line-beginning-position) + (line-end-position))) + (save-excursion + (let ((innermost-paren (progn + (goto-char (line-end-position)) + (python-info-ppss-context 'paren)))) + (when (and innermost-paren + (and (<= (line-beginning-position) innermost-paren) + (>= (line-end-position) innermost-paren))) + (goto-char innermost-paren) + (looking-at (python-rx open-paren (* space) line-end))))) + (save-excursion + (back-to-indentation) + (python-info-ppss-context 'paren)))))) (defun python-info-block-continuation-line-p () "Return non-nil if current line is a continuation of a block." @@ -2351,7 +2358,7 @@ not inside a defun." (defun python-info-ppss-context (type &optional syntax-ppss) "Return non-nil if point is on TYPE using SYNTAX-PPSS. -TYPE can be 'comment, 'string or 'parent. It returns the start +TYPE can be 'comment, 'string or 'paren. It returns the start character address of the specified TYPE." (let ((ppss (or syntax-ppss (syntax-ppss)))) (case type @@ -2364,6 +2371,20 @@ character address of the specified TYPE." (nth 1 ppss)) (t nil)))) +(defun python-info-ppss-context-type (&optional syntax-ppss) + "Return the context type using SYNTAX-PPSS. +The type returned can be 'comment, 'string or 'paren." + (let ((ppss (or syntax-ppss (syntax-ppss)))) + (cond + ((and (nth 4 ppss) + (nth 8 ppss)) + 'comment) + ((nth 8 ppss) + 'string) + ((nth 1 ppss) + 'paren) + (t nil)))) + ;;; Utility functions -- cgit v1.2.1 From 257b0017ef0d33618554664b999a2a18850a39bd Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:22 -0300 Subject: Fixed cornercase for normal lines when indentation was triggered in the middle of them --- lisp/progmodes/python.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index cdf8419f288..03524812a2d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -572,7 +572,7 @@ START is the buffer position where the sexp starts." (let ((block-regexp (python-rx block-start)) (block-start-line-end ":[[:space:]]*$")) (back-to-indentation) - (while (and (forward-comment -9999) (not (bobp)))) + (forward-comment -9999) (back-to-indentation) (when (or (python-info-continuation-line-p) (and (not (looking-at block-regexp)) @@ -595,7 +595,8 @@ START is the buffer position where the sexp starts." 'after-beginning-of-block) ;; After normal line ((setq start (save-excursion - (while (and (forward-comment -9999) (not (bobp)))) + (back-to-indentation) + (forward-comment -9999) (python-nav-sentence-start) (point-marker))) 'after-line) -- cgit v1.2.1 From 77afb61ab38a795508b0e7767442ae8fea64fbc2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:22 -0300 Subject: Refactored run-python and run-python-internal. Created new function python-shell-make-comint that takes care of creating comint processes. New Function: * python-shell-make-comint --- lisp/progmodes/python.el | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 03524812a2d..3f53d25e0d3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1245,6 +1245,24 @@ variable. 'python-shell-completion-complete-or-indent) (compilation-shell-minor-mode 1)) +(defun python-shell-make-comint (cmd proc-name) + "Create a python shell comint buffer. +CMD is the pythone command to be executed and PROC-NAME is the +process name the comint buffer will get. After the comint buffer +is created the `inferior-python-mode' is activated and the buffer +is shown." + (save-excursion + (let* ((proc-buffer-name (format "*%s*" proc-name)) + (process-environment (python-shell-calculate-process-enviroment)) + (exec-path (python-shell-calculate-exec-path))) + (when (not (comint-check-proc proc-buffer-name)) + (let ((cmdlist (split-string-and-unquote cmd))) + (set-buffer + (apply 'make-comint proc-name (car cmdlist) nil + (cdr cmdlist))) + (inferior-python-mode))) + (pop-to-buffer proc-buffer-name)))) + (defun run-python (dedicated cmd) "Run an inferior Python process. Input and output via buffer named after @@ -1264,17 +1282,7 @@ run). (y-or-n-p "Make dedicated process? ") (read-string "Run Python: " (python-shell-parse-command))) (list nil (python-shell-parse-command)))) - (let* ((proc-name (python-shell-get-process-name dedicated)) - (proc-buffer-name (format "*%s*" proc-name)) - (process-environment (python-shell-calculate-process-enviroment)) - (exec-path (python-shell-calculate-exec-path))) - (when (not (comint-check-proc proc-buffer-name)) - (let ((cmdlist (split-string-and-unquote cmd))) - (set-buffer - (apply 'make-comint proc-name (car cmdlist) nil - (cdr cmdlist))) - (inferior-python-mode))) - (pop-to-buffer proc-buffer-name)) + (python-shell-make-comint cmd (python-shell-get-process-name dedicated)) dedicated) (defun run-python-internal () @@ -1292,18 +1300,9 @@ with user shells. Runs the hook run). \(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive) - (save-excursion - (let* ((cmd (python-shell-parse-command)) - (proc-name (python-shell-internal-get-process-name)) - (proc-buffer-name (format "*%s*" proc-name)) - (process-environment (python-shell-calculate-process-enviroment)) - (exec-path (python-shell-calculate-exec-path))) - (when (not (comint-check-proc proc-buffer-name)) - (let ((cmdlist (split-string-and-unquote cmd))) - (set-buffer - (apply 'make-comint proc-name (car cmdlist) nil - (cdr cmdlist))) - (inferior-python-mode)))))) + (python-shell-make-comint + (python-shell-parse-command) + (python-shell-internal-get-process-name))) (defun python-shell-get-process () "Get inferior Python process for current buffer and return it." -- cgit v1.2.1 From 0f55249e18cab573efe672d907d6d9936beb4f1c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:23 -0300 Subject: Use defcustom with :safe whenever is possible. --- lisp/progmodes/python.el | 85 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3f53d25e0d3..55d854ca8a1 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -486,7 +486,8 @@ It makes underscores and dots word constituent chars.") (defcustom python-indent-guess-indent-offset t "Non-nil tells Python mode to guess `python-indent-offset' value." :type 'boolean - :group 'python) + :group 'python + :safe 'booleanp) (defvar python-indent-current-level 0 "Current indentation level `python-indent-line-function' is using.") @@ -1017,8 +1018,11 @@ With negative argument, move backward repeatedly to start of sentence." ;;; Shell integration -(defvar python-shell-buffer-name "Python" - "Default buffer name for Python interpreter.") +(defcustom python-shell-buffer-name "Python" + "Default buffer name for Python interpreter." + :type 'string + :group 'python + :safe 'stringp) (defcustom python-shell-interpreter "python" "Default Python interpreter for shell." @@ -1026,8 +1030,11 @@ With negative argument, move backward repeatedly to start of sentence." :group 'python :safe 'stringp) -(defvar python-shell-internal-buffer-name "Python Internal" - "Default buffer name for the Internal Python interpreter.") +(defcustom python-shell-internal-buffer-name "Python Internal" + "Default buffer name for the Internal Python interpreter." + :type 'string + :group 'python + :safe 'stringp) (defcustom python-shell-interpreter-args "-i" "Default arguments for the Python interpreter." @@ -1049,7 +1056,7 @@ It should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) -(defcustom python-shell-prompt-output-regexp nil +(defcustom python-shell-prompt-output-regexp "" "Regular Expression matching output prompt of python shell. It should not contain a caret (^) at the beginning." :type 'string @@ -1067,9 +1074,9 @@ It should not contain a caret (^) at the beginning." "Seconds to wait for process output before code setup. If output is received before the especified time then control is returned in that moment and not after waiting." - :type 'number + :type 'integer :group 'python - :safe 'numberp) + :safe 'integerp) (defcustom python-shell-process-environment nil "List of enviroment variables for Python shell. @@ -1486,7 +1493,7 @@ This function takes the list of setup code to send from the ;;; Shell completion -(defvar python-shell-completion-setup-code +(defcustom python-shell-completion-setup-code "try: import readline except ImportError: @@ -1507,11 +1514,17 @@ else: except NameError: pass return completions" - "Code used to setup completion in inferior Python processes.") + "Code used to setup completion in inferior Python processes." + :type 'string + :group 'python + :safe 'stringp) -(defvar python-shell-completion-string-code +(defcustom python-shell-completion-string-code "';'.join(__COMPLETER_all_completions('''%s'''))\n" - "Python code used to get a string of completions separated by semicolons.") + "Python code used to get a string of completions separated by semicolons." + :type 'string + :group 'python + :safe 'stringp) (defun python-shell-completion--get-completions (input process) "Retrieve available completions for INPUT using PROCESS." @@ -1569,14 +1582,17 @@ to complete." ;;; PDB Track integration -(defvar python-pdbtrack-stacktrace-info-regexp +(defcustom python-pdbtrack-stacktrace-info-regexp "> %s(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" "Regular Expression matching stacktrace information. -Used to extract the current line and module beign inspected. The +Used to extract the current line and module being inspected. The regexp should not start with a caret (^) and can contain a string placeholder (\%s) which is replaced with the filename beign inspected (so other files in the debugging process are not -opened)") +opened)" + :type 'string + :group 'python + :safe 'stringp) (defvar python-pdbtrack-tracking-buffers '() "Alist containing elements of form (# . #). @@ -1822,11 +1838,11 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." This happens when pressing \"if\", for example, to prompt for the if condition." :type 'boolean - :group 'python) + :group 'python + :safe 'booleanp) (defvar python-skeleton-available '() "Internal list of available skeletons.") -(make-variable-buffer-local 'inferior-python-mode-current-file) (define-abbrev-table 'python-mode-abbrev-table () "Abbrev table for Python mode." @@ -1948,7 +1964,7 @@ The skeleton will be bound to python-skeleton-NAME." ;;; FFAP -(defvar python-ffap-setup-code +(defcustom python-ffap-setup-code "def __FFAP_get_module_path(module): try: import os @@ -1958,11 +1974,17 @@ The skeleton will be bound to python-skeleton-NAME." return path except: return ''" - "Python code to get a module path.") + "Python code to get a module path." + :type 'string + :group 'python + :safe 'stringp) -(defvar python-ffap-string-code +(defcustom python-ffap-string-code "__FFAP_get_module_path('''%s''')\n" - "Python code used to get a string with the path of a module.") + "Python code used to get a string with the path of a module." + :type 'string + :group 'python + :safe 'stringp) (defun python-ffap-module-path (module) "Function for `ffap-alist' to return path for MODULE." @@ -1986,9 +2008,12 @@ The skeleton will be bound to python-skeleton-NAME." ;;; Code check -(defvar python-check-command +(defcustom python-check-command "pychecker --stdlib" - "Command used to check a Python file.") + "Command used to check a Python file." + :type 'string + :group 'python + :safe 'stringp) (defvar python-check-custom-command nil "Internal use.") @@ -2014,7 +2039,7 @@ Runs COMMAND, a shell command, as if by `compile'. See ;;; Eldoc -(defvar python-eldoc-setup-code +(defcustom python-eldoc-setup-code "def __PYDOC_get_help(obj): try: import inspect @@ -2045,11 +2070,17 @@ Runs COMMAND, a shell command, as if by `compile'. See exec('print doc') except SyntaxError: print(doc)" - "Python code to setup documentation retrieval.") + "Python code to setup documentation retrieval." + :type 'string + :group 'python + :safe 'stringp) -(defvar python-eldoc-string-code +(defcustom python-eldoc-string-code "__PYDOC_get_help('''%s''')\n" - "Python code used to get a string with the documentation of an object.") + "Python code used to get a string with the documentation of an object." + :type 'string + :group 'python + :safe 'stringp) (defun python-eldoc--get-doc-at-point (&optional force-input force-process) "Internal implementation to get documentation at point. -- cgit v1.2.1 From 96eeb83a55aaa75fd58ebc631736ecfeb0f5067d Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:23 -0300 Subject: Make shells inherit variables from parent buffer and internal shells not show by default. python-shell-make-comint now passes all parent buffer variables to the comint buffer created ensuring local variables work as intended. Also, this function now receives a third optional argument called POP that establishes if the created comint buffer should be displayed or not after its creation. --- lisp/progmodes/python.el | 63 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 55d854ca8a1..8c80640ec82 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1252,23 +1252,64 @@ variable. 'python-shell-completion-complete-or-indent) (compilation-shell-minor-mode 1)) -(defun python-shell-make-comint (cmd proc-name) +(defun python-shell-make-comint (cmd proc-name &optional pop) "Create a python shell comint buffer. -CMD is the pythone command to be executed and PROC-NAME is the +CMD is the python command to be executed and PROC-NAME is the process name the comint buffer will get. After the comint buffer -is created the `inferior-python-mode' is activated and the buffer -is shown." +is created the `inferior-python-mode' is activated. If POP is +non-nil the buffer is shown." (save-excursion (let* ((proc-buffer-name (format "*%s*" proc-name)) (process-environment (python-shell-calculate-process-enviroment)) (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) - (let ((cmdlist (split-string-and-unquote cmd))) - (set-buffer - (apply 'make-comint proc-name (car cmdlist) nil - (cdr cmdlist))) - (inferior-python-mode))) - (pop-to-buffer proc-buffer-name)))) + (let* ((cmdlist (split-string-and-unquote cmd)) + (buffer (apply 'make-comint proc-name (car cmdlist) nil + (cdr cmdlist))) + (python-shell-interpreter-1 python-shell-interpreter) + (python-shell-interpreter-args-1 python-shell-interpreter-args) + (python-shell-prompt-regexp-1 python-shell-prompt-regexp) + (python-shell-prompt-output-regexp-1 + python-shell-prompt-output-regexp) + (python-shell-prompt-block-regexp-1 + python-shell-prompt-block-regexp) + (python-shell-completion-setup-code-1 + python-shell-completion-setup-code) + (python-shell-completion-string-code-1 + python-shell-completion-string-code) + (python-eldoc-setup-code-1 python-eldoc-setup-code) + (python-eldoc-string-code-1 python-eldoc-string-code) + (python-ffap-setup-code-1 python-ffap-setup-code) + (python-ffap-string-code-1 python-ffap-string-code) + (python-shell-setup-codes-1 python-shell-setup-codes)) + (with-current-buffer buffer + (inferior-python-mode) + (set (make-local-variable 'python-shell-interpreter) + python-shell-interpreter-1) + (set (make-local-variable 'python-shell-interpreter-args) + python-shell-interpreter-args-1) + (set (make-local-variable 'python-shell-prompt-regexp) + python-shell-prompt-regexp-1) + (set (make-local-variable 'python-shell-prompt-output-regexp) + python-shell-prompt-output-regexp-1) + (set (make-local-variable 'python-shell-prompt-block-regexp) + python-shell-prompt-block-regexp-1) + (set (make-local-variable 'python-shell-completion-setup-code) + python-shell-completion-setup-code-1) + (set (make-local-variable 'python-shell-completion-string-code) + python-shell-completion-string-code-1) + (set (make-local-variable 'python-eldoc-setup-code) + python-eldoc-setup-code-1) + (set (make-local-variable 'python-eldoc-string-code) + python-eldoc-string-code-1) + (set (make-local-variable 'python-ffap-setup-code) + python-ffap-setup-code-1) + (set (make-local-variable 'python-ffap-string-code) + python-ffap-string-code-1) + (set (make-local-variable 'python-shell-setup-codes) + python-shell-setup-codes-1)))) + (when pop + (pop-to-buffer proc-buffer-name))))) (defun run-python (dedicated cmd) "Run an inferior Python process. @@ -1289,7 +1330,7 @@ run). (y-or-n-p "Make dedicated process? ") (read-string "Run Python: " (python-shell-parse-command))) (list nil (python-shell-parse-command)))) - (python-shell-make-comint cmd (python-shell-get-process-name dedicated)) + (python-shell-make-comint cmd (python-shell-get-process-name dedicated) t) dedicated) (defun run-python-internal () -- cgit v1.2.1 From 307bd2e8c72c33811fa6bf32a23b08fda84b282e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:24 -0300 Subject: Fix typo enviroment => environment Removed function: * python-shell-calculate-process-enviroment New function: * python-shell-calculate-process-environment --- lisp/progmodes/python.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8c80640ec82..3d25f0b46c8 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -89,7 +89,7 @@ ;; virtualenvs and other special environment modifications thanks to ;; `python-shell-process-environment' and `python-shell-exec-path'. ;; These two variables allows you to modify execution paths and -;; enviroment variables to make easy for you to setup virtualenv rules +;; environment variables to make easy for you to setup virtualenv rules ;; or behavior modifications when running shells. Here is an example ;; of how to make shell processes to be run using the /path/to/env/ ;; virtualenv: @@ -1079,11 +1079,11 @@ returned in that moment and not after waiting." :safe 'integerp) (defcustom python-shell-process-environment nil - "List of enviroment variables for Python shell. -This variable follows the same rules as `process-enviroment' + "List of environment variables for Python shell. +This variable follows the same rules as `process-environment' since it merges with it before the process creation routines are called. When this variable is nil, the Python shell is run with -the default `process-enviroment'." +the default `process-environment'." :type '(repeat string) :group 'python :safe 'listp) @@ -1171,8 +1171,8 @@ uniqueness for different types of configurations." "Calculate the string used to execute the inferior Python process." (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) -(defun python-shell-calculate-process-enviroment () - "Calculate process enviroment given `python-shell-virtualenv-path'." +(defun python-shell-calculate-process-environment () + "Calculate process environment given `python-shell-virtualenv-path'." (let ((env (python-util-merge 'list python-shell-process-environment process-environment 'string=)) (virtualenv (if python-shell-virtualenv-path @@ -1260,7 +1260,7 @@ is created the `inferior-python-mode' is activated. If POP is non-nil the buffer is shown." (save-excursion (let* ((proc-buffer-name (format "*%s*" proc-name)) - (process-environment (python-shell-calculate-process-enviroment)) + (process-environment (python-shell-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let* ((cmdlist (split-string-and-unquote cmd)) -- cgit v1.2.1 From d2190c5795c2848f144ab034b4b397d51c89f5c4 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:24 -0300 Subject: New function python-clone-local-variables Copied from org.el: it allows the `python-shell-make-comint' to be simplified. It copies all local variables from a buffer to the current. --- lisp/progmodes/python.el | 56 ++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3d25f0b46c8..7ded2c897a8 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1266,48 +1266,10 @@ non-nil the buffer is shown." (let* ((cmdlist (split-string-and-unquote cmd)) (buffer (apply 'make-comint proc-name (car cmdlist) nil (cdr cmdlist))) - (python-shell-interpreter-1 python-shell-interpreter) - (python-shell-interpreter-args-1 python-shell-interpreter-args) - (python-shell-prompt-regexp-1 python-shell-prompt-regexp) - (python-shell-prompt-output-regexp-1 - python-shell-prompt-output-regexp) - (python-shell-prompt-block-regexp-1 - python-shell-prompt-block-regexp) - (python-shell-completion-setup-code-1 - python-shell-completion-setup-code) - (python-shell-completion-string-code-1 - python-shell-completion-string-code) - (python-eldoc-setup-code-1 python-eldoc-setup-code) - (python-eldoc-string-code-1 python-eldoc-string-code) - (python-ffap-setup-code-1 python-ffap-setup-code) - (python-ffap-string-code-1 python-ffap-string-code) - (python-shell-setup-codes-1 python-shell-setup-codes)) + (current-buffer (current-buffer))) (with-current-buffer buffer (inferior-python-mode) - (set (make-local-variable 'python-shell-interpreter) - python-shell-interpreter-1) - (set (make-local-variable 'python-shell-interpreter-args) - python-shell-interpreter-args-1) - (set (make-local-variable 'python-shell-prompt-regexp) - python-shell-prompt-regexp-1) - (set (make-local-variable 'python-shell-prompt-output-regexp) - python-shell-prompt-output-regexp-1) - (set (make-local-variable 'python-shell-prompt-block-regexp) - python-shell-prompt-block-regexp-1) - (set (make-local-variable 'python-shell-completion-setup-code) - python-shell-completion-setup-code-1) - (set (make-local-variable 'python-shell-completion-string-code) - python-shell-completion-string-code-1) - (set (make-local-variable 'python-eldoc-setup-code) - python-eldoc-setup-code-1) - (set (make-local-variable 'python-eldoc-string-code) - python-eldoc-string-code-1) - (set (make-local-variable 'python-ffap-setup-code) - python-ffap-setup-code-1) - (set (make-local-variable 'python-ffap-string-code) - python-ffap-string-code-1) - (set (make-local-variable 'python-shell-setup-codes) - python-shell-setup-codes-1)))) + (python-clone-local-variables current-buffer)))) (when pop (pop-to-buffer proc-buffer-name))))) @@ -2480,6 +2442,20 @@ Return the index of the matching item, or nil if not found." (when member-result (- (length seq) (length member-result))))) +;; Stolen from org-mode +(defun python-clone-local-variables (from-buffer &optional regexp) + "Clone local variables from FROM-BUFFER. +Optional argument REGEXP selects variables to clone and defaults +to \"^python-\"." + (mapc + (lambda (pair) + (and (symbolp (car pair)) + (string-match (or regexp "^python-") + (symbol-name (car pair))) + (set (make-local-variable (car pair)) + (cdr pair)))) + (buffer-local-variables from-buffer))) + ;;;###autoload (define-derived-mode python-mode fundamental-mode "Python" -- cgit v1.2.1 From fbc39529f0c8d2ca06feb6d2082f9d1edfb7fe4b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:24 -0300 Subject: Renamed python-clone-local-variables to python-util-clone-local-variables --- lisp/progmodes/python.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 7ded2c897a8..fbd6fdd4005 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1269,7 +1269,7 @@ non-nil the buffer is shown." (current-buffer (current-buffer))) (with-current-buffer buffer (inferior-python-mode) - (python-clone-local-variables current-buffer)))) + (python-util-clone-local-variables current-buffer)))) (when pop (pop-to-buffer proc-buffer-name))))) @@ -2443,7 +2443,7 @@ Return the index of the matching item, or nil if not found." (- (length seq) (length member-result))))) ;; Stolen from org-mode -(defun python-clone-local-variables (from-buffer &optional regexp) +(defun python-util-clone-local-variables (from-buffer &optional regexp) "Clone local variables from FROM-BUFFER. Optional argument REGEXP selects variables to clone and defaults to \"^python-\"." -- cgit v1.2.1 From fad22dec878e8dc263a71348587c4ee182a4ea9b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:25 -0300 Subject: Fixed indentation case for after beginning of block --- lisp/progmodes/python.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index fbd6fdd4005..e6faf73547e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -584,6 +584,7 @@ START is the buffer position where the sexp starts." (while (and (forward-line -1) (python-info-continuation-line-p) (not (bobp)))) + (back-to-indentation) (when (not (looking-at block-regexp)) (forward-line 1))) (back-to-indentation) @@ -591,7 +592,9 @@ START is the buffer position where the sexp starts." (or (re-search-forward block-start-line-end (line-end-position) t) - (python-info-continuation-line-p))) + (save-excursion + (goto-char (line-end-position)) + (python-info-continuation-line-p)))) (point-marker))))) 'after-beginning-of-block) ;; After normal line -- cgit v1.2.1 From a0686d71f63440b119bcf671eea7409f2476bef9 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:25 -0300 Subject: Make run-python-internal to set process-query-on-exit-flag to nil Also python-shell-make-comint now returns the process buffer name. --- lisp/progmodes/python.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e6faf73547e..53bb9939dee 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1274,7 +1274,8 @@ non-nil the buffer is shown." (inferior-python-mode) (python-util-clone-local-variables current-buffer)))) (when pop - (pop-to-buffer proc-buffer-name))))) + (pop-to-buffer proc-buffer-name)) + proc-buffer-name))) (defun run-python (dedicated cmd) "Run an inferior Python process. @@ -1313,9 +1314,11 @@ with user shells. Runs the hook run). \(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive) - (python-shell-make-comint - (python-shell-parse-command) - (python-shell-internal-get-process-name))) + (set-process-query-on-exit-flag + (get-buffer-process + (python-shell-make-comint + (python-shell-parse-command) + (python-shell-internal-get-process-name))) nil)) (defun python-shell-get-process () "Get inferior Python process for current buffer and return it." -- cgit v1.2.1 From 1faf2911d613ac1c208e7ffca52e484804d3b0bb Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:25 -0300 Subject: Enhancements to python-shell-send-string-no-output to work OK with iPython 0.11 Also the documentation has been updated with info to make iPython 0.11 and 0.10 work with python.el's shell integration. --- lisp/progmodes/python.el | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 53bb9939dee..fe026a8da1a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -68,7 +68,7 @@ ;; `python-shell-completion-string-code'. ;; Here is a complete example of the settings you would use for -;; iPython +;; iPython 0.11: ;; (setq ;; python-shell-interpreter "ipython" @@ -77,7 +77,13 @@ ;; python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " ;; python-shell-completion-setup-code "" ;; python-shell-completion-string-code -;; "';'.join(__IP.complete('''%s'''))\n") +;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") + +;; For iPython 0.10 everything would be the same except for +;; `python-shell-completion-string-code': + +;; (setq python-shell-completion-string-code +;; "';'.join(__IP.complete('''%s'''))\n") ;; Please note that the default completion system depends on the ;; readline module, so if you are using some Operating System that @@ -1393,24 +1399,16 @@ the output." ""))))) (python-shell-send-string string process msg) (accept-process-output process) - ;; Cleanup output prompt regexp - (when (and (not (string= "" output-buffer)) - (> (length python-shell-prompt-output-regexp) 0)) - (setq output-buffer - (with-temp-buffer - (insert output-buffer) - (goto-char (point-min)) - (forward-comment 9999) - (buffer-substring-no-properties - (or - (and (looking-at python-shell-prompt-output-regexp) - (re-search-forward - python-shell-prompt-output-regexp nil t 1)) - (point-marker)) - (point-max))))) (mapconcat (lambda (string) string) - (butlast (split-string output-buffer "\n")) "\n"))) + (split-string + output-buffer + (if (> (length python-shell-prompt-output-regexp) 0) + (format "\n*%s$\\|^%s" + python-shell-prompt-regexp + (or python-shell-prompt-output-regexp "")) + (format "\n$\\|^%s" + python-shell-prompt-regexp)) t) "\n"))) (defun python-shell-internal-send-string (string) "Send STRING to the Internal Python interpreter. -- cgit v1.2.1 From fc6c545e4c61b341fd433f994c193ec6f61abbdd Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:25 -0300 Subject: Added simple way of jumping to current buffer's class/function definitions The new `python-nav-jump-to-defun' function allows a user to jump fast and easy to a function or class definition in the current buffer. For this a entry in the menu and the C-c C-j keybinding have been introduced. New functions: + `python-nav-list-defun-positions' + `python-nav-read-defun' + `python-nav-jump-to-defun' The new function `python-nav-list-defun-positions' does the same as a relevant part of the `python-imenu-create-index' so the latter has been refactored to use it. --- lisp/progmodes/python.el | 89 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 22 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index fe026a8da1a..05e999a6bef 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -49,14 +49,16 @@ ;; causes the current line to be dedented automatically if needed. ;; Movement: `beginning-of-defun' and `end-of-defun' functions are -;; properly implemented. Also there are specialized +;; properly implemented. There are also specialized ;; `forward-sentence' and `backward-sentence' replacements ;; (`python-nav-forward-sentence', `python-nav-backward-sentence' ;; respectively). Extra functions `python-nav-sentence-start' and ;; `python-nav-sentence-end' are included to move to the beginning and ;; to the end of a setence while taking care of multiline definitions. +;; `python-nav-jump-to-defun' is provided and allows jumping to a +;; function or class definition quickly in the current buffer. -;; Shell interaction: is provided and allows you easily execute any +;; Shell interaction: is provided and allows you to execute easily any ;; block of code of your current buffer in an inferior Python process. ;; Shell completion: hitting tab will try to complete the current @@ -213,6 +215,7 @@ (substitute-key-definition 'forward-sentence 'python-nav-forward-sentence map global-map) + (define-key map "\C-c\C-j" 'python-nav-jump-to-defun) ;; Indent specific (define-key map "\177" 'python-indent-dedent-line-backspace) (define-key map (kbd "") 'python-indent-dedent-line) @@ -247,16 +250,17 @@ ["Shift region right" python-indent-shift-right :active mark-active :help "Shift region right by a single indentation step"] "-" - ["Mark def/class" mark-defun - :help "Mark outermost definition around point"] - "-" ["Start of def/class" beginning-of-defun :help "Go to start of outermost definition around point"] ["End of def/class" end-of-defun :help "Go to end of definition around point"] - "-" + ["Mark def/class" mark-defun + :help "Mark outermost definition around point"] + ["Jump to def/class" python-nav-jump-to-defun + :help "Jump to a class or function definition"] + "--" ("Skeletons") - "-" + "---" ["Start interpreter" run-python :help "Run inferior Python process in a separate buffer"] ["Switch to shell" python-shell-switch-to-shell @@ -272,7 +276,7 @@ ["Eval file" python-shell-send-file :help "Eval file in inferior Python session"] ["Debugger" pdb :help "Run pdb under GUD"] - "-" + "----" ["Check file" python-check :help "Check file for errors"] ["Help on symbol" python-eldoc-at-point @@ -898,7 +902,7 @@ With numeric ARG, just insert that many colons. With (defvar python-nav-beginning-of-defun-regexp (python-rx line-start (* space) defun (+ space) (group symbol-name)) - "Regular expresion matching beginning of class or function. + "Regexp matching class or function definition. The name of the defun should be grouped so it can be retrieved via `match-string'.") @@ -1024,6 +1028,51 @@ With negative argument, move backward repeatedly to start of sentence." (forward-line -1) (setq arg (1+ arg)))) +(defun python-nav-list-defun-positions (&optional include-type) + "Make an Alist of defun names and point markers for current buffer. +When optional argument INCLUDE-TYPE is non-nil the type is +included the defun name." + (let ((defs)) + (save-restriction + (widen) + (save-excursion + (goto-char (point-max)) + (while (re-search-backward python-nav-beginning-of-defun-regexp nil t) + (when (and (not (python-info-ppss-context 'string)) + (not (python-info-ppss-context 'comment)) + (not (python-info-ppss-context 'parent))) + (add-to-list + 'defs (cons + (python-info-current-defun include-type) + (point-marker))))) + defs)))) + +(defun python-nav-read-defun () + "Read a defun name of current buffer and return its point marker. +A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned +when defun is completed, else nil." + (let ((defs (python-nav-list-defun-positions))) + (minibuffer-with-setup-hook + (lambda () + (setq minibuffer-completion-table (mapcar 'car defs))) + (let ((stringdef + (read-from-minibuffer + "Jump to definition: " nil + minibuffer-local-must-match-map))) + (when (not (string= stringdef "")) + (assoc-string stringdef defs)))))) + +(defun python-nav-jump-to-defun (def) + "Jump to the definition of DEF in current file." + (interactive + (list (python-nav-read-defun))) + (when (not (called-interactively-p 'interactive)) + (setq def (assoc-string def (python-nav-list-defun-positions)))) + (let ((def-marker (cdr def))) + (when (markerp def-marker) + (goto-char (marker-position def-marker)) + (back-to-indentation)))) + ;;; Shell integration @@ -2223,7 +2272,7 @@ Argument PLAIN-INDEX is the calculated plain index used to build the tree." full-element plain-index)))) (defun python-imenu-make-tree (index) -"Build the imenu alist tree from plain INDEX. + "Build the imenu alist tree from plain INDEX. The idea of this function is that given the alist: @@ -2249,21 +2298,17 @@ This tree gets built: Internally it uses `python-imenu-make-element-tree' to create all branches for each element." -(setq python-imenu-index-alist nil) -(mapc (lambda (element) - (python-imenu-make-element-tree element element index)) - (mapcar (lambda (element) - (split-string (car element) "\\." t)) index)) -python-imenu-index-alist) + (setq python-imenu-index-alist nil) + (mapc (lambda (element) + (python-imenu-make-element-tree element element index)) + (mapcar (lambda (element) + (split-string (car element) "\\." t)) index)) + python-imenu-index-alist) (defun python-imenu-create-index () "`imenu-create-index-function' for Python." - (let ((index)) - (goto-char (point-max)) - (while (python-beginning-of-defun-function 1 t) - (let ((defun-dotted-name - (python-info-current-defun python-imenu-include-defun-type))) - (push (cons defun-dotted-name (point)) index))) + (let ((index + (python-nav-list-defun-positions python-imenu-include-defun-type))) (if python-imenu-make-tree (python-imenu-make-tree index) index))) -- cgit v1.2.1 From 40417cb37ad7b4d088d68773d21e8cc8d6f1a5d3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:26 -0300 Subject: Enhancements on `python-shell-calculate-process-environment' and `python-shell-calculate-exec-path' Removed functions: + python-util-merge --- lisp/progmodes/python.el | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 05e999a6bef..28b5eeaa33f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1231,27 +1231,24 @@ uniqueness for different types of configurations." (defun python-shell-calculate-process-environment () "Calculate process environment given `python-shell-virtualenv-path'." - (let ((env (python-util-merge 'list python-shell-process-environment - process-environment 'string=)) + (let ((process-environment (append + python-shell-process-environment + process-environment nil)) (virtualenv (if python-shell-virtualenv-path (directory-file-name python-shell-virtualenv-path) nil))) (if (not virtualenv) - env - (dolist (envvar env) - (let* ((split (split-string envvar "=" t)) - (name (nth 0 split)) - (value (nth 1 split))) - (when (not (string= name "PYTHONHOME")) - (when (string= name "PATH") - (setq value (format "%s/bin:%s" virtualenv value))) - (setq env (cons (format "%s=%s" name value) env))))) - (cons (format "VIRTUAL_ENV=%s" virtualenv) env)))) + process-environment + (setenv "PYTHONHOME" nil) + (setenv "PATH" (format "%s/bin%s%s" + virtualenv path-separator (getenv "PATH"))) + (setenv "VIRTUAL_ENV" virtualenv)) + process-environment)) (defun python-shell-calculate-exec-path () "Calculate exec path given `python-shell-virtualenv-path'." - (let ((path (python-util-merge 'list python-shell-exec-path - exec-path 'string=))) + (let ((path (append python-shell-exec-path + exec-path nil))) (if (not python-shell-virtualenv-path) path (cons (format "%s/bin" @@ -2471,19 +2468,6 @@ The type returned can be 'comment, 'string or 'paren." ;;; Utility functions -;; Stolen from GNUS -(defun python-util-merge (type list1 list2 pred) - "Destructively merge lists to produce a new one. -Argument TYPE is for compatibility and ignored. LIST1 and LIST2 -are the list to be merged. Ordering of the elements is preserved -according to PRED, a `less-than' predicate on the elements." - (let ((res nil)) - (while (and list1 list2) - (if (funcall pred (car list2) (car list1)) - (push (pop list2) res) - (push (pop list1) res))) - (nconc (nreverse res) list1 list2))) - (defun python-util-position (item seq) "Find the first occurrence of ITEM in SEQ. Return the index of the matching item, or nil if not found." -- cgit v1.2.1 From 929036b470d1a5924b70cb21b943c3050cbc8b70 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:27 -0300 Subject: Enhancements to `python-shell-calculate-process-environment' The `python-shell-extra-pythonpaths' variable have been introduced as simple way of adding paths to the PYTHONPATH without affecting existing values. --- lisp/progmodes/python.el | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 28b5eeaa33f..1c05048512a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -122,6 +122,10 @@ ;; (setq python-shell-virtualenv-path "/path/to/env/") +;; Also the `python-shell-extra-pythonpaths' variable have been +;; introduced as simple way of adding paths to the PYTHONPATH without +;; affecting existing values. + ;; Pdb tracking: when you execute a block of code that contains some ;; call to pdb (or ipdb) it will prompt the block of code and will ;; follow the execution of pdb marking the current line with an arrow. @@ -1146,6 +1150,14 @@ the default `process-environment'." :group 'python :safe 'listp) +(defcustom python-shell-extra-pythonpaths nil + "List of extra pythonpaths for Python shell. +The values of this variable are added to the existing value of +PYTHONPATH in the `process-environment' variable." + :type '(repeat string) + :group 'python + :safe 'listp) + (defcustom python-shell-exec-path nil "List of path to search for binaries. This variable follows the same rules as `exec-path' since it @@ -1237,11 +1249,20 @@ uniqueness for different types of configurations." (virtualenv (if python-shell-virtualenv-path (directory-file-name python-shell-virtualenv-path) nil))) + (when python-shell-extra-pythonpaths + (setenv "PYTHONPATH" + (format "%s%s%s" + (mapconcat 'identity + python-shell-extra-pythonpaths + path-separator) + path-separator + (or (getenv "PYTHONPATH") "")))) (if (not virtualenv) process-environment (setenv "PYTHONHOME" nil) (setenv "PATH" (format "%s/bin%s%s" - virtualenv path-separator (getenv "PATH"))) + virtualenv path-separator + (or (getenv "PATH") ""))) (setenv "VIRTUAL_ENV" virtualenv)) process-environment)) -- cgit v1.2.1 From b71bfa9cd6e670a97a803adb1027a859cd66bfe9 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:27 -0300 Subject: Return appropriate value to comint-dynamic-complete This avoids a "No completions for ..." message being issued when a completion has been made. Also, reduces redundancy between the inferior-python-mode and python-mode completion code. --- lisp/progmodes/python.el | 54 +++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1c05048512a..4fe5bd87462 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1609,38 +1609,38 @@ else: (when (> (length completions) 2) (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) -(defun python-shell-completion--get-completion (input completions) - "Get completion for INPUT using COMPLETIONS." - (let ((completion (when completions +(defun python-shell-completion--do-completion-at-point (process) + "Do completion for INPUT using COMPLETIONS." + (with-syntax-table python-dotty-syntax-table + (let* ((input (substring-no-properties + (or (comint-word (current-word)) "") nil nil)) + (completions (python-shell-completion--get-completions + input process)) + (completion (when completions (try-completion input completions)))) - (cond ((eq completion t) - input) - ((null completion) - (message "Can't find completion for \"%s\"" input) - (ding) - input) + (cond ((eq completion t) + t) + ((null completion) + (message "Can't find completion for \"%s\"" input) + (ding) + nil) ((not (string= input completion)) - completion) + (progn (delete-char (- (length input))) + (insert completion) + t)) (t - (message "Making completion list...") (with-output-to-temp-buffer "*Python Completions*" (display-completion-list (all-completions input completions))) - input)))) + t))))) (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." (interactive) - (with-syntax-table python-dotty-syntax-table - (when (and comint-last-prompt-overlay - (> (point-marker) (overlay-end comint-last-prompt-overlay))) - (let* ((process (get-buffer-process (current-buffer))) - (input (substring-no-properties - (or (comint-word (current-word)) "") nil nil))) - (delete-char (- (length input))) - (insert - (python-shell-completion--get-completion - input (python-shell-completion--get-completions input process))))))) + (and comint-last-prompt-overlay + (> (point-marker) (overlay-end comint-last-prompt-overlay)) + (python-shell-completion--do-completion-at-point + (get-buffer-process (current-buffer))))) (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. @@ -1749,15 +1749,7 @@ inferior python process is updated properly." (let ((process (python-shell-get-process))) (if (not process) (error "Completion needs an inferior Python process running") - (with-syntax-table python-dotty-syntax-table - (let* ((input (substring-no-properties - (or (comint-word (current-word)) "") nil nil)) - (completions (python-shell-completion--get-completions - input process))) - (delete-char (- (length input))) - (insert - (python-shell-completion--get-completion - input completions))))))) + (python-shell-completion--do-completion-at-point process)))) (add-to-list 'debug-ignored-errors "^Completion needs an inferior Python process running.") -- cgit v1.2.1 From 9253ea694decb0d5a5995b1a59db3274db290ac8 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:27 -0300 Subject: Complete module import lines in addition to variable names Available when using ipython v0.11. Completions on lines starting with "from " or "import " are supplied by IPython.core.completerlib.module_completion ipython v0.11 configuration: (setq python-shell-interpreter "ipython" python-shell-completion-setup-code "from IPython.core.completerlib import module_completion\n" python-shell-module-completion-string-code "';'.join(module_completion('''%s'''))\n" python-shell-completion-string-code "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") --- lisp/progmodes/python.el | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4fe5bd87462..72df547d760 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1600,24 +1600,48 @@ else: :group 'python :safe 'stringp) -(defun python-shell-completion--get-completions (input process) +(defcustom python-shell-module-completion-string-code "" + "Python code used to get a string of completions separated by + semicolons on a module import line. + +For IPython v0.11, add the following line to +`python-shell-completion-setup-code': + +from IPython.core.completerlib import module_completion + +and use the following as the value of this variable: + +';'.join(module_completion('''%s'''))\n" + :type 'string + :group 'python + :safe 'stringp) + +(defvar python-shell-import-line-regexp "^\\(from\\|import\\)[ \t]") + +(defun python-shell-completion--get-completions (input process completion-code) "Retrieve available completions for INPUT using PROCESS." (with-current-buffer (process-buffer process) (let ((completions (python-shell-send-string-no-output - (format python-shell-completion-string-code input) - process))) + (format completion-code input) process))) (when (> (length completions) 2) (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) (defun python-shell-completion--do-completion-at-point (process) "Do completion for INPUT using COMPLETIONS." (with-syntax-table python-dotty-syntax-table - (let* ((input (substring-no-properties - (or (comint-word (current-word)) "") nil nil)) - (completions (python-shell-completion--get-completions - input process)) - (completion (when completions - (try-completion input completions)))) + (let* ((line (substring-no-properties + (buffer-substring (point-at-bol) (point)) nil nil)) + (input (substring-no-properties + (or (comint-word (current-word)) "") nil nil)) + (completions + (if (and (> (length python-shell-module-completion-string-code) 0) + (string-match python-shell-import-line-regexp line)) + (python-shell-completion--get-completions + line process python-shell-module-completion-string-code) + (python-shell-completion--get-completions + input process python-shell-completion-string-code))) + (completion (when completions + (try-completion input completions)))) (cond ((eq completion t) t) ((null completion) -- cgit v1.2.1 From 8386d830a364dcd08e7dccaa480e8f7e6bb3fb9d Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:28 -0300 Subject: Documentation enhancements and other small fixes. Removed vars: + python-shell-import-line-regexp --- lisp/progmodes/python.el | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 72df547d760..52c9b628b4f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1601,8 +1601,7 @@ else: :safe 'stringp) (defcustom python-shell-module-completion-string-code "" - "Python code used to get a string of completions separated by - semicolons on a module import line. + "Python code used to get completions separated by semicolons for imports. For IPython v0.11, add the following line to `python-shell-completion-setup-code': @@ -1616,10 +1615,10 @@ and use the following as the value of this variable: :group 'python :safe 'stringp) -(defvar python-shell-import-line-regexp "^\\(from\\|import\\)[ \t]") - (defun python-shell-completion--get-completions (input process completion-code) - "Retrieve available completions for INPUT using PROCESS." + "Retrieve available completions for INPUT using PROCESS. +Argument COMPLETION-CODE is the python code used to get +completions on the current context." (with-current-buffer (process-buffer process) (let ((completions (python-shell-send-string-no-output (format completion-code input) process))) @@ -1627,7 +1626,7 @@ and use the following as the value of this variable: (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) (defun python-shell-completion--do-completion-at-point (process) - "Do completion for INPUT using COMPLETIONS." + "Do completion at point for PROCESS." (with-syntax-table python-dotty-syntax-table (let* ((line (substring-no-properties (buffer-substring (point-at-bol) (point)) nil nil)) @@ -1635,7 +1634,7 @@ and use the following as the value of this variable: (or (comint-word (current-word)) "") nil nil)) (completions (if (and (> (length python-shell-module-completion-string-code) 0) - (string-match python-shell-import-line-regexp line)) + (string-match "^\\(from\\|import\\)[ \t]" line)) (python-shell-completion--get-completions line process python-shell-module-completion-string-code) (python-shell-completion--get-completions -- cgit v1.2.1 From aeac8c27f2bb01fef164634747e0f5bb7cf6702e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:28 -0300 Subject: Enhancements to pdbtrack pdbtracking now autodetects the filename being tracked from the prompt and not from the `inferior-python-mode-current-file' variable. Removed vars: + `inferior-python-mode-current-file' --- lisp/progmodes/python.el | 107 +++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 59 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 52c9b628b4f..89f454ed95b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1281,10 +1281,6 @@ uniqueness for different types of configurations." OUTPUT is a string with the contents of the buffer." (ansi-color-filter-apply output)) -(defvar inferior-python-mode-current-file nil - "Current file from which a region was sent.") -(make-variable-buffer-local 'inferior-python-mode-current-file) - (define-derived-mode inferior-python-mode comint-mode "Inferior Python" "Major mode for Python inferior process. Runs a Python interpreter as a subprocess of Emacs, with Python @@ -1530,9 +1526,6 @@ FILE-NAME." (file-name (or (expand-file-name file-name) temp-file-name))) (when (not file-name) (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) - (with-current-buffer (process-buffer process) - (setq inferior-python-mode-current-file - (convert-standard-filename file-name))) (python-shell-send-string (format (concat "__pyfile = open('''%s''');" @@ -1680,81 +1673,77 @@ to complete." ;;; PDB Track integration (defcustom python-pdbtrack-stacktrace-info-regexp - "> %s(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "^> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" "Regular Expression matching stacktrace information. -Used to extract the current line and module being inspected. The -regexp should not start with a caret (^) and can contain a string -placeholder (\%s) which is replaced with the filename beign -inspected (so other files in the debugging process are not -opened)" +Used to extract the current line and module being inspected." :type 'string :group 'python :safe 'stringp) -(defvar python-pdbtrack-tracking-buffers '() +(defvar python-pdbtrack-tracking-buffers nil "Alist containing elements of form (# . #). The car of each element of the alist is the tracking buffer and the cdr is the tracked buffer.") -(defun python-pdbtrack-get-or-add-tracking-buffers () - "Get/Add a tracked buffer for the current buffer. +(defun python-pdbtrack-get-or-add-tracking-buffers (file-name) + "Get/Add a tracked buffer for the current buffer and FILE-NAME. Internally it uses the `python-pdbtrack-tracking-buffers' alist. Returns a cons with the form: * (# . #< tracked buffer>)." - (or - (assq (current-buffer) python-pdbtrack-tracking-buffers) - (let* ((file (with-current-buffer (current-buffer) - inferior-python-mode-current-file)) - (tracking-buffers - `(,(current-buffer) . - ,(or (get-file-buffer file) - (find-file-noselect file))))) - (set-buffer (cdr tracking-buffers)) - (python-mode) - (set-buffer (car tracking-buffers)) - (setq python-pdbtrack-tracking-buffers - (cons tracking-buffers python-pdbtrack-tracking-buffers)) - tracking-buffers))) + (let ((tracking-buffers + (cons (current-buffer) + (or (get-file-buffer file-name) + (find-file-noselect file-name))))) + (set-buffer (cdr tracking-buffers)) + (when (not (eq major-mode 'python-mode)) + (python-mode)) + (set-buffer (car tracking-buffers)) + (assq-delete-all (current-buffer) python-pdbtrack-tracking-buffers) + (setq python-pdbtrack-tracking-buffers + (cons tracking-buffers python-pdbtrack-tracking-buffers)) + tracking-buffers)) (defun python-pdbtrack-comint-output-filter-function (output) "Move overlay arrow to current pdb line in tracked buffer. Argument OUTPUT is a string with the output from the comint process." (when (not (string= output "")) - (let ((full-output (ansi-color-filter-apply - (buffer-substring comint-last-input-end - (point-max))))) - (if (string-match python-shell-prompt-pdb-regexp full-output) - (let* ((tracking-buffers (python-pdbtrack-get-or-add-tracking-buffers)) - (line-num - (save-excursion - (string-match - (format python-pdbtrack-stacktrace-info-regexp - (regexp-quote - inferior-python-mode-current-file)) - full-output) - (string-to-number (or (match-string-no-properties 1 full-output) "")))) - (tracked-buffer-window (get-buffer-window (cdr tracking-buffers))) + (let* ((full-output (ansi-color-filter-apply + (buffer-substring comint-last-input-end (point-max)))) + (line-number) + (file-name + (with-temp-buffer + ;; OK, this sucks but for some reason + ;; string-match was not doing his trick. + (insert full-output) + (goto-char (point-min)) + (when (looking-at python-pdbtrack-stacktrace-info-regexp) + (setq line-number (string-to-number + (match-string-no-properties 2))) + (match-string-no-properties 1))))) + (if (and file-name line-number) + (let* ((tracking-buffers + (python-pdbtrack-get-or-add-tracking-buffers file-name)) + (tracked-buffer-window + (get-buffer-window (cdr tracking-buffers))) (tracked-buffer-line-pos)) - (when line-num - (with-current-buffer (cdr tracking-buffers) - (set (make-local-variable 'overlay-arrow-string) "=>") - (set (make-local-variable 'overlay-arrow-position) (make-marker)) - (setq tracked-buffer-line-pos (progn - (goto-char (point-min)) - (forward-line (1- line-num)) - (point-marker))) - (when tracked-buffer-window - (set-window-point tracked-buffer-window tracked-buffer-line-pos)) - (set-marker overlay-arrow-position tracked-buffer-line-pos))) + (with-current-buffer (cdr tracking-buffers) + (set (make-local-variable 'overlay-arrow-string) "=>") + (set (make-local-variable 'overlay-arrow-position) (make-marker)) + (setq tracked-buffer-line-pos (progn + (goto-char (point-min)) + (forward-line (1- line-number)) + (point-marker))) + (when tracked-buffer-window + (set-window-point + tracked-buffer-window tracked-buffer-line-pos)) + (set-marker overlay-arrow-position tracked-buffer-line-pos)) (pop-to-buffer (cdr tracking-buffers)) (switch-to-buffer-other-window (car tracking-buffers))) (let ((tracking-buffers (assq (current-buffer) python-pdbtrack-tracking-buffers))) (when tracking-buffers - (if inferior-python-mode-current-file - (with-current-buffer (cdr tracking-buffers) - (set-marker overlay-arrow-position nil)) - (kill-buffer (cdr tracking-buffers))) + (with-current-buffer (cdr tracking-buffers) + (set-marker overlay-arrow-position nil)) (setq python-pdbtrack-tracking-buffers (assq-delete-all (current-buffer) python-pdbtrack-tracking-buffers))))))) -- cgit v1.2.1 From e1f00930bf11bfcfca0d7efd7503ab04b8f08d6a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:28 -0300 Subject: Enhancements to pdbtrack. pdbtrack now handles correctly the case where the stacktrace information is on the second line. All python buffers not opened by the user and used for tracking are closed automatically when tracking process finishes. Simplified code for keeping the tracked buffer. Removed vars: + python-pdbtrack-tracking-buffers Removed functions: + python-pdbtrack-get-or-add-tracking-buffers New vars: + python-pdbtrack-tracked-buffer + python-pdbtrack-buffers-to-kill New functions: + python-pdbtrack-set-tracked-buffer --- lisp/progmodes/python.el | 82 +++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 40 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 89f454ed95b..17afb0ee309 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1680,28 +1680,27 @@ Used to extract the current line and module being inspected." :group 'python :safe 'stringp) -(defvar python-pdbtrack-tracking-buffers nil - "Alist containing elements of form (# . #). -The car of each element of the alist is the tracking buffer and -the cdr is the tracked buffer.") - -(defun python-pdbtrack-get-or-add-tracking-buffers (file-name) - "Get/Add a tracked buffer for the current buffer and FILE-NAME. -Internally it uses the `python-pdbtrack-tracking-buffers' alist. -Returns a cons with the form: - * (# . #< tracked buffer>)." - (let ((tracking-buffers - (cons (current-buffer) - (or (get-file-buffer file-name) - (find-file-noselect file-name))))) - (set-buffer (cdr tracking-buffers)) - (when (not (eq major-mode 'python-mode)) - (python-mode)) - (set-buffer (car tracking-buffers)) - (assq-delete-all (current-buffer) python-pdbtrack-tracking-buffers) - (setq python-pdbtrack-tracking-buffers - (cons tracking-buffers python-pdbtrack-tracking-buffers)) - tracking-buffers)) +(defvar python-pdbtrack-tracked-buffer nil + "Variable containing the value of the current tracked buffer. +Never set this variable directly, use +`python-pdbtrack-set-tracked-buffer' instead.") +(make-variable-buffer-local 'python-pdbtrack-tracked-buffer) + +(defvar python-pdbtrack-buffers-to-kill nil + "List of buffers to be deleted after tracking finishes.") +(make-variable-buffer-local 'python-pdbtrack-buffers-to-kill) + +(defun python-pdbtrack-set-tracked-buffer (file-name) + "Set the buffer for FILE-NAME as the tracked buffer. +Internally it uses the `python-pdbtrack-tracked-buffer' variable. +Returns the tracked buffer." + (let ((file-buffer (get-file-buffer file-name))) + (if file-buffer + (setq python-pdbtrack-tracked-buffer file-buffer) + (setq file-buffer (find-file-noselect file-name)) + (when (not (member file-buffer python-pdbtrack-buffers-to-kill)) + (add-to-list 'python-pdbtrack-buffers-to-kill file-buffer))) + file-buffer)) (defun python-pdbtrack-comint-output-filter-function (output) "Move overlay arrow to current pdb line in tracked buffer. @@ -1712,21 +1711,24 @@ Argument OUTPUT is a string with the output from the comint process." (line-number) (file-name (with-temp-buffer - ;; OK, this sucks but for some reason - ;; string-match was not doing his trick. (insert full-output) (goto-char (point-min)) - (when (looking-at python-pdbtrack-stacktrace-info-regexp) + ;; OK, this sucked but now it became a cool hack. The + ;; stacktrace information normally is on the first line + ;; but in some cases (like when doing a step-in) it is + ;; on the second. + (when (or (looking-at python-pdbtrack-stacktrace-info-regexp) + (and (forward-line) + (looking-at python-pdbtrack-stacktrace-info-regexp))) (setq line-number (string-to-number (match-string-no-properties 2))) (match-string-no-properties 1))))) (if (and file-name line-number) - (let* ((tracking-buffers - (python-pdbtrack-get-or-add-tracking-buffers file-name)) - (tracked-buffer-window - (get-buffer-window (cdr tracking-buffers))) + (let* ((tracked-buffer (python-pdbtrack-set-tracked-buffer file-name)) + (shell-buffer (current-buffer)) + (tracked-buffer-window (get-buffer-window tracked-buffer)) (tracked-buffer-line-pos)) - (with-current-buffer (cdr tracking-buffers) + (with-current-buffer tracked-buffer (set (make-local-variable 'overlay-arrow-string) "=>") (set (make-local-variable 'overlay-arrow-position) (make-marker)) (setq tracked-buffer-line-pos (progn @@ -1737,16 +1739,16 @@ Argument OUTPUT is a string with the output from the comint process." (set-window-point tracked-buffer-window tracked-buffer-line-pos)) (set-marker overlay-arrow-position tracked-buffer-line-pos)) - (pop-to-buffer (cdr tracking-buffers)) - (switch-to-buffer-other-window (car tracking-buffers))) - (let ((tracking-buffers (assq (current-buffer) - python-pdbtrack-tracking-buffers))) - (when tracking-buffers - (with-current-buffer (cdr tracking-buffers) - (set-marker overlay-arrow-position nil)) - (setq python-pdbtrack-tracking-buffers - (assq-delete-all (current-buffer) - python-pdbtrack-tracking-buffers))))))) + (pop-to-buffer tracked-buffer) + (switch-to-buffer-other-window shell-buffer)) + (when python-pdbtrack-tracked-buffer + (with-current-buffer python-pdbtrack-tracked-buffer + (set-marker overlay-arrow-position nil)) + (mapc #'(lambda (buffer) + (ignore-errors (kill-buffer buffer))) + python-pdbtrack-buffers-to-kill) + (setq python-pdbtrack-tracked-buffer nil + python-pdbtrack-buffers-to-kill nil))))) output) -- cgit v1.2.1 From 279c9272ee87a6bdbf40ac8ddb0cbffa09a9afc6 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:29 -0300 Subject: Enhancements to internal python shell naming. The internal python shell now takes into account more shell settings in order to generate the unique hash for its name. Also `python-shell-setup-codes' has been simplified and is not allowed to be a list of cons anymore. --- lisp/progmodes/python.el | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 17afb0ee309..777436cf89b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1181,11 +1181,7 @@ virtualenv." (defcustom python-shell-setup-codes '(python-shell-completion-setup-code python-ffap-setup-code python-eldoc-setup-code) - "List of code run by `python-shell-send-setup-codes'. -Each variable can contain either a simple string with the code to -execute or a cons with the form (CODE . DESCRIPTION), where CODE -is a string with the code to execute and DESCRIPTION is the -description of it." + "List of code run by `python-shell-send-setup-codes'." :type '(repeat symbol) :group 'python :safe 'listp) @@ -1232,8 +1228,13 @@ uniqueness for different types of configurations." (md5 (concat (python-shell-parse-command) + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-output-regexp (mapconcat #'symbol-value python-shell-setup-codes "") (mapconcat #'indentity python-shell-process-environment "") + (mapconcat #'indentity python-shell-extra-pythonpaths "") + (mapconcat #'indentity python-shell-exec-path "") (or python-shell-virtualenv-path "") (mapconcat #'indentity python-shell-exec-path ""))))) @@ -1548,8 +1549,6 @@ This function takes the list of setup code to send from the (accept-process-output process python-shell-send-setup-max-wait) (dolist (code python-shell-setup-codes) (when code - (when (consp code) - (setq msg (cdr code))) (message (format msg code)) (python-shell-send-string-no-output (symbol-value code) process))))) -- cgit v1.2.1 From 2bdce3883b7a3014aea3ae3a9decf77ecee8dd40 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:29 -0300 Subject: Renamed indentity occurrences to identity in previous commmit --- lisp/progmodes/python.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 777436cf89b..f5b75686f50 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1232,11 +1232,11 @@ uniqueness for different types of configurations." python-shell-prompt-block-regexp python-shell-prompt-output-regexp (mapconcat #'symbol-value python-shell-setup-codes "") - (mapconcat #'indentity python-shell-process-environment "") - (mapconcat #'indentity python-shell-extra-pythonpaths "") - (mapconcat #'indentity python-shell-exec-path "") + (mapconcat #'identity python-shell-process-environment "") + (mapconcat #'identity python-shell-extra-pythonpaths "") + (mapconcat #'identity python-shell-exec-path "") (or python-shell-virtualenv-path "") - (mapconcat #'indentity python-shell-exec-path ""))))) + (mapconcat #'identity python-shell-exec-path ""))))) (defun python-shell-parse-command () "Calculate the string used to execute the inferior Python process." -- cgit v1.2.1 From 9399498ea73905a1b6c35ad2238119534a90769f Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:29 -0300 Subject: Documentation enhancements --- lisp/progmodes/python.el | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f5b75686f50..73b4776f7ab 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -77,9 +77,12 @@ ;; python-shell-interpreter-args "" ;; python-shell-prompt-regexp "In \\[[0-9]+\\]: " ;; python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " -;; python-shell-completion-setup-code "" +;; python-shell-completion-setup-code +;; "from IPython.core.completerlib import module_completion" +;; python-shell-module-completion-string-code +;; "';'.join(module_completion('''%s'''))\n" ;; python-shell-completion-string-code -;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") +;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") ;; For iPython 0.10 everything would be the same except for ;; `python-shell-completion-string-code': @@ -1292,10 +1295,11 @@ controls which Python interpreter is run. Variables `python-shell-prompt-output-regexp', `python-shell-prompt-block-regexp', `python-shell-completion-setup-code', -`python-shell-completion-string-code', `python-eldoc-setup-code', -`python-eldoc-string-code', `python-ffap-setup-code' and -`python-ffap-string-code' can customize this mode for different -Python interpreters. +`python-shell-completion-string-code', +`python-shell-module-completion-string-code', +`python-eldoc-setup-code', `python-eldoc-string-code', +`python-ffap-setup-code' and `python-ffap-string-code' can +customize this mode for different Python interpreters. You can also add additional setup code to be run at initialization of the interpreter via `python-shell-setup-codes' -- cgit v1.2.1 From 291e2b939b2240a748a33f298cb6188de4c79f3b Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:30 -0300 Subject: Dismiss completions buffer when unique completion has been made --- lisp/progmodes/python.el | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 73b4776f7ab..a2f1ed4e809 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1611,6 +1611,8 @@ and use the following as the value of this variable: :group 'python :safe 'stringp) +(defvar python-completion-original-window-configuration nil) + (defun python-shell-completion--get-completions (input process completion-code) "Retrieve available completions for INPUT using PROCESS. Argument COMPLETION-CODE is the python code used to get @@ -1638,7 +1640,12 @@ completions on the current context." (completion (when completions (try-completion input completions)))) (cond ((eq completion t) - t) + (if (eq this-command last-command) + (when python-completion-original-window-configuration + (set-window-configuration + python-completion-original-window-configuration))) + (setq python-completion-original-window-configuration nil) + t) ((null completion) (message "Can't find completion for \"%s\"" input) (ding) @@ -1648,6 +1655,9 @@ completions on the current context." (insert completion) t)) (t + (unless python-completion-original-window-configuration + (setq python-completion-original-window-configuration + (current-window-configuration))) (with-output-to-temp-buffer "*Python Completions*" (display-completion-list (all-completions input completions))) -- cgit v1.2.1 From c7815c384be68b5ec6fbf3207ec59eb45cb21896 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:30 -0300 Subject: Make `python-shell-send-string-no-output' delete trailing newlines from output. --- lisp/progmodes/python.el | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a2f1ed4e809..f75b5cb51ae 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1467,16 +1467,14 @@ the output." ""))))) (python-shell-send-string string process msg) (accept-process-output process) - (mapconcat - (lambda (string) string) - (split-string - output-buffer - (if (> (length python-shell-prompt-output-regexp) 0) - (format "\n*%s$\\|^%s" - python-shell-prompt-regexp - (or python-shell-prompt-output-regexp "")) - (format "\n$\\|^%s" - python-shell-prompt-regexp)) t) "\n"))) + (replace-regexp-in-string + (if (> (length python-shell-prompt-output-regexp) 0) + (format "\n*%s$\\|^%s\\|\n$" + python-shell-prompt-regexp + (or python-shell-prompt-output-regexp "")) + (format "\n*$\\|^%s\\|\n$" + python-shell-prompt-regexp)) + "" output-buffer))) (defun python-shell-internal-send-string (string) "Send STRING to the Internal Python interpreter. -- cgit v1.2.1 From 9a5fc059730c7c28808b8ead58c8b1d370670a8d Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:30 -0300 Subject: Don't request completions for zero-length input Fixes a bug in which incorrect completion output was displayed, for example when point was after a closing paren. --- lisp/progmodes/python.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f75b5cb51ae..47bdef2c928 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1633,8 +1633,9 @@ completions on the current context." (string-match "^\\(from\\|import\\)[ \t]" line)) (python-shell-completion--get-completions line process python-shell-module-completion-string-code) - (python-shell-completion--get-completions - input process python-shell-completion-string-code))) + (and (> (length input) 0) + (python-shell-completion--get-completions + input process python-shell-completion-string-code)))) (completion (when completions (try-completion input completions)))) (cond ((eq completion t) -- cgit v1.2.1 From f6b59cd19050e3ef07cef9480aa4a032982eb591 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:31 -0300 Subject: Renamed stuff to keep "namespace" clean. Renamed vars: + python-shell-module-completion-string-code => python-shell-completion-module-string-code + python-completion-original-window-configuration => python-shell-completion-original-window-configuration --- lisp/progmodes/python.el | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 47bdef2c928..ce6382cf225 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -79,7 +79,7 @@ ;; python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: " ;; python-shell-completion-setup-code ;; "from IPython.core.completerlib import module_completion" -;; python-shell-module-completion-string-code +;; python-shell-completion-module-string-code ;; "';'.join(module_completion('''%s'''))\n" ;; python-shell-completion-string-code ;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") @@ -1296,7 +1296,7 @@ controls which Python interpreter is run. Variables `python-shell-prompt-block-regexp', `python-shell-completion-setup-code', `python-shell-completion-string-code', -`python-shell-module-completion-string-code', +`python-shell-completion-module-string-code', `python-eldoc-setup-code', `python-eldoc-string-code', `python-ffap-setup-code' and `python-ffap-string-code' can customize this mode for different Python interpreters. @@ -1594,7 +1594,7 @@ else: :group 'python :safe 'stringp) -(defcustom python-shell-module-completion-string-code "" +(defcustom python-shell-completion-module-string-code "" "Python code used to get completions separated by semicolons for imports. For IPython v0.11, add the following line to @@ -1609,7 +1609,7 @@ and use the following as the value of this variable: :group 'python :safe 'stringp) -(defvar python-completion-original-window-configuration nil) +(defvar python-shell-completion-original-window-configuration nil) (defun python-shell-completion--get-completions (input process completion-code) "Retrieve available completions for INPUT using PROCESS. @@ -1629,10 +1629,10 @@ completions on the current context." (input (substring-no-properties (or (comint-word (current-word)) "") nil nil)) (completions - (if (and (> (length python-shell-module-completion-string-code) 0) + (if (and (> (length python-shell-completion-module-string-code) 0) (string-match "^\\(from\\|import\\)[ \t]" line)) (python-shell-completion--get-completions - line process python-shell-module-completion-string-code) + line process python-shell-completion-module-string-code) (and (> (length input) 0) (python-shell-completion--get-completions input process python-shell-completion-string-code)))) @@ -1640,10 +1640,10 @@ completions on the current context." (try-completion input completions)))) (cond ((eq completion t) (if (eq this-command last-command) - (when python-completion-original-window-configuration + (when python-shell-completion-original-window-configuration (set-window-configuration - python-completion-original-window-configuration))) - (setq python-completion-original-window-configuration nil) + python-shell-completion-original-window-configuration))) + (setq python-shell-completion-original-window-configuration nil) t) ((null completion) (message "Can't find completion for \"%s\"" input) @@ -1654,8 +1654,8 @@ completions on the current context." (insert completion) t)) (t - (unless python-completion-original-window-configuration - (setq python-completion-original-window-configuration + (unless python-shell-completion-original-window-configuration + (setq python-shell-completion-original-window-configuration (current-window-configuration))) (with-output-to-temp-buffer "*Python Completions*" (display-completion-list -- cgit v1.2.1 From 76a9ea3bbb02add4edca960f0314a42e85e0069a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:31 -0300 Subject: Added `python-pdbtrack-activate' variable to allow users to activate/deactivate pdbtrack. To keep in sync with the old behavior its default value is t. --- lisp/progmodes/python.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ce6382cf225..401d9fd0495 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1684,6 +1684,12 @@ to complete." ;;; PDB Track integration +(defcustom python-pdbtrack-activate t + "Non-nil makes python shell enable pdbtracking." + :type 'boolean + :group 'python + :safe 'booleanp) + (defcustom python-pdbtrack-stacktrace-info-regexp "^> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" "Regular Expression matching stacktrace information. @@ -1717,7 +1723,7 @@ Returns the tracked buffer." (defun python-pdbtrack-comint-output-filter-function (output) "Move overlay arrow to current pdb line in tracked buffer. Argument OUTPUT is a string with the output from the comint process." - (when (not (string= output "")) + (when (and python-pdbtrack-activate (not (string= output ""))) (let* ((full-output (ansi-color-filter-apply (buffer-substring comint-last-input-end (point-max)))) (line-number) -- cgit v1.2.1 From aa4099353c10e0af0bbc11ca56a8be6243b3c882 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:31 -0300 Subject: Improved completion support when in [i]pdb `python-shell-completion--do-completion-at-point' has been modified in order to support different completion contexts easily. New vars: + python-shell-completion-pdb-string-code --- lisp/progmodes/python.el | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 401d9fd0495..b4d388eb41d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1609,6 +1609,13 @@ and use the following as the value of this variable: :group 'python :safe 'stringp) +(defcustom python-shell-completion-pdb-string-code + "';'.join(globals().keys() + locals().keys())" + "Python code used to get completions separated by semicolons for [i]pdb." + :type 'string + :group 'python + :safe 'stringp) + (defvar python-shell-completion-original-window-configuration nil) (defun python-shell-completion--get-completions (input process completion-code) @@ -1628,14 +1635,20 @@ completions on the current context." (buffer-substring (point-at-bol) (point)) nil nil)) (input (substring-no-properties (or (comint-word (current-word)) "") nil nil)) - (completions - (if (and (> (length python-shell-completion-module-string-code) 0) + (completion-code + (cond ((and (> (length python-shell-completion-pdb-string-code) 0) + (string-match python-shell-prompt-pdb-regexp + (buffer-substring-no-properties + (overlay-start comint-last-prompt-overlay) + (overlay-end comint-last-prompt-overlay)))) + python-shell-completion-pdb-string-code) + ((and (> (length python-shell-completion-module-string-code) 0) (string-match "^\\(from\\|import\\)[ \t]" line)) - (python-shell-completion--get-completions - line process python-shell-completion-module-string-code) - (and (> (length input) 0) - (python-shell-completion--get-completions - input process python-shell-completion-string-code)))) + python-shell-completion-module-string-code) + (t python-shell-completion-string-code))) + (completions + (and (> (length input) 0) + (python-shell-completion--get-completions line process completion-code))) (completion (when completions (try-completion input completions)))) (cond ((eq completion t) -- cgit v1.2.1 From 338a21d0d138a008c985a82d650f8b4a1b5b35d3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:32 -0300 Subject: Improvements on completion code. Do no complete when defining a code block (we can't do this until finding some way of getting raw tabs to the interpreter correctly) --- lisp/progmodes/python.el | 49 +++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b4d388eb41d..5842dc021c6 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1635,20 +1635,27 @@ completions on the current context." (buffer-substring (point-at-bol) (point)) nil nil)) (input (substring-no-properties (or (comint-word (current-word)) "") nil nil)) + (prompt (buffer-substring-no-properties + (overlay-start comint-last-prompt-overlay) + (overlay-end comint-last-prompt-overlay))) (completion-code (cond ((and (> (length python-shell-completion-pdb-string-code) 0) - (string-match python-shell-prompt-pdb-regexp - (buffer-substring-no-properties - (overlay-start comint-last-prompt-overlay) - (overlay-end comint-last-prompt-overlay)))) + (string-match + (concat "^" python-shell-prompt-pdb-regexp) prompt)) python-shell-completion-pdb-string-code) ((and (> (length python-shell-completion-module-string-code) 0) - (string-match "^\\(from\\|import\\)[ \t]" line)) + (string-match + (concat "^" python-shell-prompt-regexp) prompt) + (string-match "^\\(from\\|import\\)[ \t]" line)) python-shell-completion-module-string-code) - (t python-shell-completion-string-code))) + ((string-match + (concat "^" python-shell-prompt-regexp) prompt) + python-shell-completion-string-code) + (t nil))) (completions - (and (> (length input) 0) - (python-shell-completion--get-completions line process completion-code))) + (and completion-code (> (length input) 0) + (python-shell-completion--get-completions + line process completion-code))) (completion (when completions (try-completion input completions)))) (cond ((eq completion t) @@ -1661,19 +1668,19 @@ completions on the current context." ((null completion) (message "Can't find completion for \"%s\"" input) (ding) - nil) - ((not (string= input completion)) - (progn (delete-char (- (length input))) - (insert completion) - t)) - (t - (unless python-shell-completion-original-window-configuration - (setq python-shell-completion-original-window-configuration - (current-window-configuration))) - (with-output-to-temp-buffer "*Python Completions*" - (display-completion-list - (all-completions input completions))) - t))))) + nil) + ((not (string= input completion)) + (progn (delete-char (- (length input))) + (insert completion) + t)) + (t + (unless python-shell-completion-original-window-configuration + (setq python-shell-completion-original-window-configuration + (current-window-configuration))) + (with-output-to-temp-buffer "*Python Completions*" + (display-completion-list + (all-completions input completions))) + t))))) (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." -- cgit v1.2.1 From 722c985bb0553b4036bff2ec7227492524320425 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:32 -0300 Subject: Added compatibility with lastest CEDET. Obsolete variables definitions: + python-buffer => python-shell-internal-buffer Obsolete functions definitions: + python-proc => python-shell-internal-get-or-create-process + python-send-string => python-shell-internal-send-string --- lisp/progmodes/python.el | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5842dc021c6..9e83ac77f6b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1426,13 +1426,26 @@ of commands.)" dedicated-proc-buffer-name global-proc-buffer-name)))) +(defvar python-shell-internal-buffer nil + "Current internal shell buffer for the current buffer. +This is really not necessary at all for the code to work but it's +there for compatibility with CEDET.") +(make-variable-buffer-local 'python-shell-internal-buffer) + (defun python-shell-internal-get-or-create-process () "Get or create an inferior Internal Python process." (let* ((proc-name (python-shell-internal-get-process-name)) (proc-buffer-name (format "*%s*" proc-name))) (run-python-internal) + (setq python-shell-internal-buffer proc-buffer-name) (get-buffer-process proc-buffer-name))) +(define-obsolete-function-alias + 'python-proc 'python-shell-internal-get-or-create-process "23.3") + +(define-obsolete-variable-alias + 'python-buffer 'python-shell-internal-buffer "23.3") + (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. When MSG is non-nil messages the first line of STRING." @@ -1486,10 +1499,10 @@ Returns the output. See `python-shell-send-string-no-output'." (python-shell-internal-get-or-create-process) nil)) (define-obsolete-function-alias - 'python-send-receive 'python-shell-internal-send-string "23.3" - "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-receive 'python-shell-internal-send-string "23.3") + +(define-obsolete-function-alias + 'python-send-string 'python-shell-internal-send-string "23.3") (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." -- cgit v1.2.1 From a1ea6ab81c362ada4899b59dbac3df0af27aa77f Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:32 -0300 Subject: Enhanced shell setup for Windows. Also added documentation explaining how to spawn the iPython process on Windows. --- lisp/progmodes/python.el | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 9e83ac77f6b..fe39ff1c4c3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -90,6 +90,18 @@ ;; (setq python-shell-completion-string-code ;; "';'.join(__IP.complete('''%s'''))\n") +;; Unfortunately running iPython on Windows needs some more tweaking. +;; The way you must set `python-shell-interpreter' and +;; `python-shell-interpreter-args' is as follows: + +;; (setq +;; python-shell-interpreter "C:\\Python27\\python.exe" +;; python-shell-interpreter-args +;; "-i C:\\Python27\\Scripts\\ipython-script.py") + +;; That will spawn the iPython process correctly (Of course you need +;; to modify the paths according to your system). + ;; Please note that the default completion system depends on the ;; readline module, so if you are using some Operating System that ;; bundles Python without it (like Windows) just install the @@ -1565,7 +1577,7 @@ This function takes the list of setup code to send from the (dolist (code python-shell-setup-codes) (when code (message (format msg code)) - (python-shell-send-string-no-output + (python-shell-send-string (symbol-value code) process))))) (add-hook 'inferior-python-mode-hook -- cgit v1.2.1 From e37a45513c00b56f1cf5c738139ee17ec1c33f0b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:33 -0300 Subject: Fixed docstring typos --- lisp/progmodes/python.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index fe39ff1c4c3..960630fc988 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1217,7 +1217,7 @@ virtualenv." :group 'python) (defun python-shell-get-process-name (dedicated) - "Calculate the appropiate process name for inferior Python process. + "Calculate the appropriate process name for inferior Python process. If DEDICATED is t and the variable `buffer-file-name' is non-nil returns a string with the form `python-shell-buffer-name'[variable `buffer-file-name'] else @@ -1234,7 +1234,7 @@ in the `same-window-buffer-names' list." process-name)) (defun python-shell-internal-get-process-name () - "Calculate the appropiate process name for Internal Python process. + "Calculate the appropriate process name for Internal Python process. The name is calculated from `python-shell-global-buffer-name' and a hash of all relevant global shell settings in order to ensure uniqueness for different types of configurations." -- cgit v1.2.1 From c4b155cb4982649123e70d89bccb439a067a5e72 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:33 -0300 Subject: Removed :safe attribute for dangerous variables Changed vars: + python-shell-interpreter + python-shell-interpreter-args + python-shell-completion-setup-code + python-shell-completion-string-code + python-shell-completion-module-string-code + python-shell-completion-pdb-string-code + python-ffap-setup-code + python-ffap-string-code + python-check-command + python-eldoc-setup-code + python-eldoc-string-code --- lisp/progmodes/python.el | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 960630fc988..def6d8bba94 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1104,8 +1104,7 @@ when defun is completed, else nil." (defcustom python-shell-interpreter "python" "Default Python interpreter for shell." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-shell-internal-buffer-name "Python Internal" "Default buffer name for the Internal Python interpreter." @@ -1116,8 +1115,7 @@ when defun is completed, else nil." (defcustom python-shell-interpreter-args "-i" "Default arguments for the Python interpreter." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-shell-prompt-regexp ">>> " "Regular Expression matching top\-level input prompt of python shell. @@ -1609,15 +1607,13 @@ else: return completions" "Code used to setup completion in inferior Python processes." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-shell-completion-string-code "';'.join(__COMPLETER_all_completions('''%s'''))\n" "Python code used to get a string of completions separated by semicolons." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-shell-completion-module-string-code "" "Python code used to get completions separated by semicolons for imports. @@ -1631,15 +1627,13 @@ and use the following as the value of this variable: ';'.join(module_completion('''%s'''))\n" :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-shell-completion-pdb-string-code "';'.join(globals().keys() + locals().keys())" "Python code used to get completions separated by semicolons for [i]pdb." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defvar python-shell-completion-original-window-configuration nil) @@ -2119,15 +2113,13 @@ The skeleton will be bound to python-skeleton-NAME." return ''" "Python code to get a module path." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-ffap-string-code "__FFAP_get_module_path('''%s''')\n" "Python code used to get a string with the path of a module." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defun python-ffap-module-path (module) "Function for `ffap-alist' to return path for MODULE." @@ -2155,8 +2147,7 @@ The skeleton will be bound to python-skeleton-NAME." "pychecker --stdlib" "Command used to check a Python file." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defvar python-check-custom-command nil "Internal use.") @@ -2215,15 +2206,13 @@ Runs COMMAND, a shell command, as if by `compile'. See print(doc)" "Python code to setup documentation retrieval." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defcustom python-eldoc-string-code "__PYDOC_get_help('''%s''')\n" "Python code used to get a string with the documentation of an object." :type 'string - :group 'python - :safe 'stringp) + :group 'python) (defun python-eldoc--get-doc-at-point (&optional force-input force-process) "Internal implementation to get documentation at point. -- cgit v1.2.1 From bba416bcf2bc4270420bf99f6a28b06f9e4e68b3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:34 -0300 Subject: Enhancements on python-check command. Use pyflakes (PyChecker is dead) and run the process respecting virtualenv rules. --- lisp/progmodes/python.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index def6d8bba94..5406d74f69e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2144,7 +2144,7 @@ The skeleton will be bound to python-skeleton-NAME." ;;; Code check (defcustom python-check-command - "pychecker --stdlib" + "pyflakes" "Command used to check a Python file." :type 'string :group 'python) @@ -2168,7 +2168,9 @@ Runs COMMAND, a shell command, as if by `compile'. See ""))))))) (setq python-check-custom-command command) (save-some-buffers (not compilation-ask-about-save) nil) - (compilation-start command)) + (let ((process-environment (python-shell-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path))) + (compilation-start command))) ;;; Eldoc -- cgit v1.2.1 From bbd27e0710f2885448949880e5b3e48c6a7b97a5 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:34 -0300 Subject: `python-check' now can have named buffers on its own depending the command launched. New vars: + python-check-buffer-name --- lisp/progmodes/python.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5406d74f69e..2f886a5b803 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2149,6 +2149,12 @@ The skeleton will be bound to python-skeleton-NAME." :type 'string :group 'python) +(defcustom python-check-buffer-name + "*Python check: %s*" + "Buffer name used for check commands." + :type 'string + :group 'python) + (defvar python-check-custom-command nil "Internal use.") @@ -2170,7 +2176,9 @@ Runs COMMAND, a shell command, as if by `compile'. See (save-some-buffers (not compilation-ask-about-save) nil) (let ((process-environment (python-shell-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) - (compilation-start command))) + (compilation-start command nil + (lambda (mode-name) + (format python-check-buffer-name command))))) ;;; Eldoc -- cgit v1.2.1 From 0674d3fadbd4c5755598dc2791a179dbbe1d018e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:34 -0300 Subject: Enhancements on indentation code and related functions. This commit includes: * A more robust implementation of `python-indent-calculate-indentation'. * enhancements on `python-indent-context' when dealing with backslashes and blocks. * Many changes, comments and enhancements to `python-indent-calculate-indentation'. Many of them especially focused to match pep8 guidelines, being this one the most important new one: http://mail.python.org/pipermail/python-dev/2011-June/111760.html * Better `python-info-line-ends-backslash-p' that would work as intended on narrowed buffers. * `python-info-continuation-line-p' now does what's supposed to do. * Enhanced implementation of `python-info-continuation-line-p', `python-info-block-continuation-line-p' and `python-info-assignment-continuation-line-p' New Functions: * `python-util-forward-comment' a simple replacement `forward-comment' with some necessary enhancements. --- lisp/progmodes/python.el | 418 +++++++++++++++++++++++++++-------------------- 1 file changed, 238 insertions(+), 180 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2f886a5b803..3244f299778 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -535,30 +535,35 @@ These make `python-indent-calculate-indentation' subtract the value of (save-restriction (widen) (goto-char (point-min)) - (let ((found-block)) - (while (and (not found-block) + (let ((block-end)) + (while (and (not block-end) (re-search-forward (python-rx line-start block-start) nil t)) - (when (and (not (python-info-ppss-context 'string)) - (not (python-info-ppss-context 'comment)) - (progn - (goto-char (line-end-position)) - (forward-comment -9999) - (eq ?: (char-before)))) - (setq found-block t))) - (if (not found-block) + (when (and + (not (python-info-ppss-context-type)) + (progn + (goto-char (line-end-position)) + (python-util-forward-comment -1) + (if (equal (char-before) ?:) + t + (forward-line 1) + (when (python-info-block-continuation-line-p) + (while (and (python-info-continuation-line-p) + (not (eobp))) + (forward-line 1)) + (python-util-forward-comment -1) + (when (equal (char-before) ?:) + t))))) + (setq block-end (point-marker)))) + (let ((indentation + (when block-end + (goto-char block-end) + (python-util-forward-comment) + (current-indentation)))) + (if indentation + (setq python-indent-offset indentation) (message "Can't guess python-indent-offset, using defaults: %s" - python-indent-offset) - (while (and (progn - (goto-char (line-end-position)) - (python-info-continuation-line-p)) - (not (eobp))) - (forward-line 1)) - (forward-line 1) - (forward-comment 9999) - (let ((indent-offset (current-indentation))) - (when (> indent-offset 0) - (setq python-indent-offset indent-offset)))))))) + python-indent-offset))))))) (defun python-indent-context () "Get information on indentation context. @@ -594,42 +599,32 @@ START is the buffer position where the sexp starts." ((setq start (when (not (or (python-info-ppss-context 'string ppss) (python-info-ppss-context 'comment ppss))) (let ((line-beg-pos (line-beginning-position))) - (when (eq ?\\ (char-before (1- line-beg-pos))) + (when (python-info-line-ends-backslash-p (1- line-beg-pos)) (- line-beg-pos 2))))) 'after-backslash) ;; After beginning of block ((setq start (save-excursion - (let ((block-regexp (python-rx block-start)) - (block-start-line-end ":[[:space:]]*$")) - (back-to-indentation) - (forward-comment -9999) - (back-to-indentation) - (when (or (python-info-continuation-line-p) - (and (not (looking-at block-regexp)) - (save-excursion - (re-search-forward - block-start-line-end - (line-end-position) t)))) - (while (and (forward-line -1) - (python-info-continuation-line-p) - (not (bobp)))) - (back-to-indentation) - (when (not (looking-at block-regexp)) - (forward-line 1))) - (back-to-indentation) - (when (and (looking-at block-regexp) - (or (re-search-forward - block-start-line-end - (line-end-position) t) - (save-excursion - (goto-char (line-end-position)) - (python-info-continuation-line-p)))) + (when (progn + (back-to-indentation) + (python-util-forward-comment -1) + (equal (char-before) ?:)) + ;; Move to the first block start that's not in within + ;; a string, comment or paren and that's not a + ;; continuation line. + (while (and (re-search-backward + (python-rx block-start) nil t) + (or + (python-info-ppss-context 'string) + (python-info-ppss-context 'comment) + (python-info-ppss-context 'paren) + (python-info-continuation-line-p)))) + (when (looking-at (python-rx block-start)) (point-marker))))) 'after-beginning-of-block) ;; After normal line ((setq start (save-excursion (back-to-indentation) - (forward-comment -9999) + (python-util-forward-comment -1) (python-nav-sentence-start) (point-marker))) 'after-line) @@ -647,9 +642,14 @@ START is the buffer position where the sexp starts." (save-excursion (case context-status ('no-indent 0) + ;; When point is after beginning of block just add one level + ;; of indentation relative to the context-start ('after-beginning-of-block (goto-char context-start) (+ (current-indentation) python-indent-offset)) + ;; When after a simple line just use previous line + ;; indentation, in the case current line starts with a + ;; `python-indent-dedenters' de-indent one level. ('after-line (- (save-excursion @@ -660,92 +660,125 @@ START is the buffer position where the sexp starts." (looking-at (regexp-opt python-indent-dedenters))) python-indent-offset 0))) + ;; When inside of a string, do nothing. just use the current + ;; indentation. XXX: perhaps it would be a good idea to + ;; invoke standard text indentation here ('inside-string (goto-char context-start) (current-indentation)) + ;; After backslash we have several posibilities ('after-backslash - (let* ((block-continuation - (save-excursion - (forward-line -1) - (python-info-block-continuation-line-p))) - (assignment-continuation - (save-excursion - (forward-line -1) - (python-info-assignment-continuation-line-p))) - (dot-continuation - (save-excursion - (back-to-indentation) - (when (looking-at "\\.") - (forward-line -1) - (goto-char (line-end-position)) - (while (and (re-search-backward "\\." (line-beginning-position) t) - (or (python-info-ppss-context 'comment) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)))) - (if (and (looking-at "\\.") - (not (or (python-info-ppss-context 'comment) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)))) - (current-column) - (+ (current-indentation) python-indent-offset))))) - (indentation (cond - (dot-continuation - dot-continuation) - (block-continuation - (goto-char block-continuation) - (re-search-forward - (python-rx block-start (* space)) - (line-end-position) t) - (current-column)) - (assignment-continuation - (goto-char assignment-continuation) - (re-search-forward - (python-rx simple-operator) - (line-end-position) t) - (forward-char 1) - (re-search-forward - (python-rx (* space)) - (line-end-position) t) - (current-column)) - (t - (goto-char context-start) - (if (not - (save-excursion - (back-to-indentation) - (looking-at - "\\(?:return\\|from\\|import\\)\s+"))) - (current-indentation) - (+ (current-indentation) - (length - (match-string-no-properties 0)))))))) - indentation)) + (cond + ;; Check if current line is a dot continuation. For this + ;; the current line must start with a dot and previous + ;; line must contain a dot too. + ((save-excursion + (back-to-indentation) + (when (looking-at "\\.") + (forward-line -1) + (goto-char (line-end-position)) + (while (and (re-search-backward "\\." (line-beginning-position) t) + (or (python-info-ppss-context 'comment) + (python-info-ppss-context 'string) + (python-info-ppss-context 'paren)))) + (if (and (looking-at "\\.") + (not (or (python-info-ppss-context 'comment) + (python-info-ppss-context 'string) + (python-info-ppss-context 'paren)))) + ;; The indentation is the same column of the + ;; first matching dot that's not inside a + ;; comment, a string or a paren + (current-column) + ;; No dot found on previous line, just add another + ;; indentation level. + (+ (current-indentation) python-indent-offset))))) + ;; Check if prev line is a block continuation + ((let ((block-continuation-start + (python-info-block-continuation-line-p))) + (when block-continuation-start + ;; If block-continuation-start is set jump to that + ;; marker and use first column after the block start + ;; as indentation value. + (goto-char block-continuation-start) + (re-search-forward + (python-rx block-start (* space)) + (line-end-position) t) + (current-column)))) + ;; Check if current line is an assignment continuation + ((let ((assignment-continuation-start + (python-info-assignment-continuation-line-p))) + (when assignment-continuation-start + ;; If assignment-continuation is set jump to that + ;; marker and use first column after the assignment + ;; operator as indentation value. + (goto-char assignment-continuation-start) + (current-column)))) + (t + (forward-line -1) + (if (save-excursion + (and + (python-info-line-ends-backslash-p) + (forward-line -1) + (python-info-line-ends-backslash-p))) + ;; The two previous lines ended in a backslash so we must + ;; respect previous line indentation. + (current-indentation) + ;; What happens here is that we are dealing with the second + ;; line of a backslash continuation, in that case we just going + ;; to add one indentation level. + (+ (current-indentation) python-indent-offset))))) + ;; When inside a paren there's a need to handle nesting + ;; correctly ('inside-paren - (or (save-excursion - (skip-syntax-forward "\s" (line-end-position)) - (when (and (looking-at (regexp-opt '(")" "]" "}"))) - (not (forward-char 1)) - (not (python-info-ppss-context 'paren))) - (goto-char context-start) + (cond + ;; If current line closes the outtermost open paren use the + ;; current indentation of the context-start line. + ((save-excursion + (skip-syntax-forward "\s" (line-end-position)) + (when (and (looking-at (regexp-opt '(")" "]" "}"))) + (progn + (forward-char 1) + (not (python-info-ppss-context 'paren)))) + (goto-char context-start) + (current-indentation)))) + ;; If open paren is contained on a line by itself add another + ;; indentation level, else look for the first word after the + ;; opening paren and use it's column position as indentation + ;; level. + ((let* ((content-starts-in-newline) + (indent + (save-excursion + (if (setq content-starts-in-newline + (progn + (goto-char context-start) + (forward-char) + (save-restriction + (narrow-to-region + (line-beginning-position) + (line-end-position)) + (python-util-forward-comment)) + (looking-at "$"))) + (+ (current-indentation) python-indent-offset) + (current-column))))) + ;; Adjustments + (cond + ;; If current line closes a nested open paren de-indent one + ;; level. + ((progn (back-to-indentation) - (current-column))) - (- - (save-excursion - (goto-char context-start) - (forward-char) - (save-restriction - (narrow-to-region - (line-beginning-position) - (line-end-position)) - (forward-comment 9999)) - (if (looking-at "$") - (+ (current-indentation) python-indent-offset) - (forward-comment 9999) - (current-column))) - (if (progn - (back-to-indentation) - (looking-at (regexp-opt '(")" "]" "}")))) - python-indent-offset - 0))))))))) + (looking-at (regexp-opt '(")" "]" "}")))) + (- indent python-indent-offset)) + ;; If the line of the opening paren that wraps the current + ;; line starts a block add another level of indentation to + ;; follow new pep8 recommendation. See: http://ur1.ca/5rojx + ((save-excursion + (when (and content-starts-in-newline + (progn + (goto-char context-start) + (back-to-indentation) + (looking-at (python-rx block-start)))) + (+ indent python-indent-offset)))) + (t indent))))))))))) (defun python-indent-calculate-levels () "Calculate `python-indent-levels' and reset `python-indent-current-level'." @@ -972,7 +1005,7 @@ decorators are not included. Return non-nil if point is moved to the (let ((found)) (dotimes (i (- arg) found) (python-end-of-defun-function) - (forward-comment 9999) + (python-util-forward-comment) (goto-char (line-end-position)) (when (not (eobp)) (setq found @@ -996,7 +1029,7 @@ Returns nil if point is not in a def or class." (not (eobp)) (or (not (current-word)) (> (current-indentation) beg-defun-indent)))) - (forward-comment 9999) + (python-util-forward-comment) (goto-char (line-beginning-position)))) (defun python-nav-sentence-start () @@ -1036,13 +1069,13 @@ With negative argument, move backward repeatedly to start of sentence." (interactive "^p") (or arg (setq arg 1)) (while (> arg 0) - (forward-comment 9999) + (python-util-forward-comment) (python-nav-sentence-end) (forward-line 1) (setq arg (1- arg))) (while (< arg 0) (python-nav-sentence-end) - (forward-comment -9999) + (python-util-forward-comment -1) (python-nav-sentence-start) (forward-line -1) (setq arg (1+ arg)))) @@ -2415,7 +2448,7 @@ not inside a defun." (widen) (save-excursion (goto-char (line-end-position)) - (forward-comment -9999) + (python-util-forward-comment -1) (setq min-indent (current-indentation)) (while (python-beginning-of-defun-function 1 t) (when (or (< (current-indentation) min-indent) @@ -2462,68 +2495,83 @@ not inside a defun." (when (member (current-word) '("except" "else")) (point-marker)))))))) -(defun python-info-line-ends-backslash-p () - "Return non-nil if current line ends with backslash." - (string= (or (ignore-errors - (buffer-substring - (line-end-position) - (- (line-end-position) 1))) "") "\\")) +(defun python-info-line-ends-backslash-p (&optional line-number) + "Return non-nil if current line ends with backslash. +With optional argument LINE-NUMBER, check that line instead." + (save-excursion + (save-restriction + (when line-number + (goto-char line-number)) + (widen) + (goto-char (line-end-position)) + (equal (char-after (1- (point))) ?\\)))) (defun python-info-continuation-line-p () - "Return non-nil if current line is continuation of another." - (let ((current-ppss-context-type (python-info-ppss-context-type))) - (and - (equal (save-excursion - (goto-char (line-end-position)) - (forward-comment 9999) - (python-info-ppss-context-type)) - current-ppss-context-type) - (or (python-info-line-ends-backslash-p) - (string-match ",[[:space:]]*$" (buffer-substring - (line-beginning-position) - (line-end-position))) - (save-excursion - (let ((innermost-paren (progn - (goto-char (line-end-position)) - (python-info-ppss-context 'paren)))) - (when (and innermost-paren - (and (<= (line-beginning-position) innermost-paren) - (>= (line-end-position) innermost-paren))) - (goto-char innermost-paren) - (looking-at (python-rx open-paren (* space) line-end))))) - (save-excursion - (back-to-indentation) - (python-info-ppss-context 'paren)))))) + "Check if current line is continuation of another. +When current line is continuation of another return the point +where the continued line ends." + (save-excursion + (save-restriction + (widen) + (let* ((context-type (progn + (back-to-indentation) + (python-info-ppss-context-type))) + (line-start (line-number-at-pos)) + (context-start (when context-type + (python-info-ppss-context context-type)))) + (cond ((equal context-type 'paren) + ;; Lines inside a paren are always a continuation line + ;; (except the first one). + (when (equal (python-info-ppss-context-type) 'paren) + (python-util-forward-comment -1) + (python-util-forward-comment -1) + (point-marker))) + ((or (equal context-type 'comment) + (equal context-type 'string)) + ;; move forward an roll again + (goto-char context-start) + (python-util-forward-comment) + (python-info-continuation-line-p)) + (t + ;; Not within a paren, string or comment, the only way we are + ;; dealing with a continuation line is that previous line + ;; contains a backslash, and this can only be the previous line + ;; from current + (back-to-indentation) + (python-util-forward-comment -1) + (python-util-forward-comment -1) + (when (and (equal (1- line-start) (line-number-at-pos)) + (python-info-line-ends-backslash-p)) + (point-marker)))))))) (defun python-info-block-continuation-line-p () "Return non-nil if current line is a continuation of a block." (save-excursion - (while (and (not (bobp)) - (python-info-continuation-line-p)) - (forward-line -1)) - (forward-line 1) - (back-to-indentation) - (when (looking-at (python-rx block-start)) - (point-marker)))) + (when (python-info-continuation-line-p) + (forward-line -1) + (back-to-indentation) + (when (looking-at (python-rx block-start)) + (point-marker))))) (defun python-info-assignment-continuation-line-p () - "Return non-nil if current line is a continuation of an assignment." + "Check if current line is a continuation of an assignment. +When current line is continuation of another with an assignment +return the point of the first non-blank character after the +operator." (save-excursion - (while (and (not (bobp)) - (python-info-continuation-line-p)) - (forward-line -1)) - (forward-line 1) - (back-to-indentation) - (when (and (not (looking-at (python-rx block-start))) - (save-excursion + (when (python-info-continuation-line-p) + (forward-line -1) + (back-to-indentation) + (when (and (not (looking-at (python-rx block-start))) (and (re-search-forward (python-rx not-simple-operator assignment-operator not-simple-operator) (line-end-position) t) (not (or (python-info-ppss-context 'string) (python-info-ppss-context 'paren) - (python-info-ppss-context 'comment)))))) - (point-marker)))) + (python-info-ppss-context 'comment))))) + (skip-syntax-forward "\s") + (point-marker))))) (defun python-info-ppss-context (type &optional syntax-ppss) "Return non-nil if point is on TYPE using SYNTAX-PPSS. @@ -2578,6 +2626,16 @@ to \"^python-\"." (cdr pair)))) (buffer-local-variables from-buffer))) +(defun python-util-forward-comment (&optional direction) + "Python mode specific version of `forward-comment'." + (let ((comment-start (python-info-ppss-context 'comment)) + (factor (if (< (or direction 0) 0) + -99999 + 99999))) + (when comment-start + (goto-char comment-start)) + (forward-comment factor))) + ;;;###autoload (define-derived-mode python-mode fundamental-mode "Python" -- cgit v1.2.1 From 6cad4c6e3015b4b7758fa8e88f853e856bf0a705 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:35 -0300 Subject: Beautified code * All columns are now less that 79 chars. * Removed tabs that were beign used to indent code. * Reindented the whole file. --- lisp/progmodes/python.el | 339 +++++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 162 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3244f299778..3dd94c3793f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -260,48 +260,48 @@ (define-key map "\C-c\C-f" 'python-eldoc-at-point) ;; Utilities (substitute-key-definition 'complete-symbol 'completion-at-point - map global-map) + map global-map) (easy-menu-define python-menu map "Python Mode menu" `("Python" - :help "Python-specific Features" - ["Shift region left" python-indent-shift-left :active mark-active - :help "Shift region left by a single indentation step"] - ["Shift region right" python-indent-shift-right :active mark-active - :help "Shift region right by a single indentation step"] - "-" - ["Start of def/class" beginning-of-defun - :help "Go to start of outermost definition around point"] - ["End of def/class" end-of-defun - :help "Go to end of definition around point"] - ["Mark def/class" mark-defun - :help "Mark outermost definition around point"] - ["Jump to def/class" python-nav-jump-to-defun - :help "Jump to a class or function definition"] + :help "Python-specific Features" + ["Shift region left" python-indent-shift-left :active mark-active + :help "Shift region left by a single indentation step"] + ["Shift region right" python-indent-shift-right :active mark-active + :help "Shift region right by a single indentation step"] + "-" + ["Start of def/class" beginning-of-defun + :help "Go to start of outermost definition around point"] + ["End of def/class" end-of-defun + :help "Go to end of definition around point"] + ["Mark def/class" mark-defun + :help "Mark outermost definition around point"] + ["Jump to def/class" python-nav-jump-to-defun + :help "Jump to a class or function definition"] "--" - ("Skeletons") + ("Skeletons") "---" - ["Start interpreter" run-python - :help "Run inferior Python process in a separate buffer"] - ["Switch to shell" python-shell-switch-to-shell - :help "Switch to running inferior Python process"] - ["Eval string" python-shell-send-string - :help "Eval string in inferior Python session"] - ["Eval buffer" python-shell-send-buffer - :help "Eval buffer in inferior Python session"] - ["Eval region" python-shell-send-region - :help "Eval region in inferior Python session"] - ["Eval defun" python-shell-send-defun - :help "Eval defun in inferior Python session"] - ["Eval file" python-shell-send-file - :help "Eval file in inferior Python session"] - ["Debugger" pdb :help "Run pdb under GUD"] + ["Start interpreter" run-python + :help "Run inferior Python process in a separate buffer"] + ["Switch to shell" python-shell-switch-to-shell + :help "Switch to running inferior Python process"] + ["Eval string" python-shell-send-string + :help "Eval string in inferior Python session"] + ["Eval buffer" python-shell-send-buffer + :help "Eval buffer in inferior Python session"] + ["Eval region" python-shell-send-region + :help "Eval region in inferior Python session"] + ["Eval defun" python-shell-send-defun + :help "Eval defun in inferior Python session"] + ["Eval file" python-shell-send-file + :help "Eval file in inferior Python session"] + ["Debugger" pdb :help "Run pdb under GUD"] "----" - ["Check file" python-check - :help "Check file for errors"] - ["Help on symbol" python-eldoc-at-point - :help "Get help on symbol at point"] - ["Complete symbol" completion-at-point - :help "Complete symbol before point"])) + ["Check file" python-check + :help "Check file for errors"] + ["Help on symbol" python-eldoc-at-point + :help "Get help on symbol at point"] + ["Complete symbol" completion-at-point + :help "Complete symbol before point"])) map) "Keymap for `python-mode'.") @@ -316,13 +316,15 @@ "except" "finally" "for" "while" "with") symbol-end)) `(decorator . ,(rx line-start (* space) ?@ (any letter ?_) - (* (any word ?_)))) + (* (any word ?_)))) `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) `(open-paren . ,(rx (or "{" "[" "("))) `(close-paren . ,(rx (or "}" "]" ")"))) `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) - `(not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) + `(not-simple-operator . ,(rx + (not + (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" "=" "%" "**" "//" "<<" ">>" "<=" "!=" "==" ">=" "is" "not"))) @@ -331,14 +333,15 @@ "Additional Python specific sexps for `python-rx'")) (defmacro python-rx (&rest regexps) - "Python mode specialized rx macro which supports common python named REGEXPS." - (let ((rx-constituents (append python-rx-constituents rx-constituents))) - (cond ((null regexps) - (error "No regexp")) - ((cdr regexps) - (rx-to-string `(and ,@regexps) t)) - (t - (rx-to-string (car regexps) t))))) + "Python mode specialized rx macro. +This variant of `rx' supports common python named REGEXPS." + (let ((rx-constituents (append python-rx-constituents rx-constituents))) + (cond ((null regexps) + (error "No regexp")) + ((cdr regexps) + (rx-to-string `(and ,@regexps) t)) + (t + (rx-to-string (car regexps) t))))) ;;; Font-lock and syntax @@ -434,7 +437,7 @@ ;; string delimiters. Fixme: Is there a better way? ;; First avoid a sequence preceded by an odd number of backslashes. `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. - "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") + "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") (3 (python-quote-syntax))))) (defun python-quote-syntax () @@ -459,8 +462,8 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)." (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 + (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) @@ -480,11 +483,11 @@ Used for syntactic keywords. N is the match number (1, 2 or 3)." ;; 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))) + (sst (standard-syntax-table))) (dotimes (i 128) - (unless (= i ?_) - (if (equal symbol (aref sst i)) - (modify-syntax-entry i "." table))))) + (unless (= i ?_) + (if (equal symbol (aref sst i)) + (modify-syntax-entry i "." table))))) (modify-syntax-entry ?$ "." table) (modify-syntax-entry ?% "." table) ;; exceptions @@ -531,39 +534,39 @@ These make `python-indent-calculate-indentation' subtract the value of (defun python-indent-guess-indent-offset () "Guess and set `python-indent-offset' for the current buffer." - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (let ((block-end)) - (while (and (not block-end) - (re-search-forward - (python-rx line-start block-start) nil t)) - (when (and - (not (python-info-ppss-context-type)) - (progn - (goto-char (line-end-position)) - (python-util-forward-comment -1) - (if (equal (char-before) ?:) - t - (forward-line 1) - (when (python-info-block-continuation-line-p) - (while (and (python-info-continuation-line-p) - (not (eobp))) - (forward-line 1)) - (python-util-forward-comment -1) - (when (equal (char-before) ?:) - t))))) - (setq block-end (point-marker)))) - (let ((indentation - (when block-end - (goto-char block-end) - (python-util-forward-comment) - (current-indentation)))) - (if indentation - (setq python-indent-offset indentation) - (message "Can't guess python-indent-offset, using defaults: %s" - python-indent-offset))))))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((block-end)) + (while (and (not block-end) + (re-search-forward + (python-rx line-start block-start) nil t)) + (when (and + (not (python-info-ppss-context-type)) + (progn + (goto-char (line-end-position)) + (python-util-forward-comment -1) + (if (equal (char-before) ?:) + t + (forward-line 1) + (when (python-info-block-continuation-line-p) + (while (and (python-info-continuation-line-p) + (not (eobp))) + (forward-line 1)) + (python-util-forward-comment -1) + (when (equal (char-before) ?:) + t))))) + (setq block-end (point-marker)))) + (let ((indentation + (when block-end + (goto-char block-end) + (python-util-forward-comment) + (current-indentation)))) + (if indentation + (setq python-indent-offset indentation) + (message "Can't guess python-indent-offset, using defaults: %s" + python-indent-offset))))))) (defun python-indent-context () "Get information on indentation context. @@ -599,7 +602,8 @@ START is the buffer position where the sexp starts." ((setq start (when (not (or (python-info-ppss-context 'string ppss) (python-info-ppss-context 'comment ppss))) (let ((line-beg-pos (line-beginning-position))) - (when (python-info-line-ends-backslash-p (1- line-beg-pos)) + (when (python-info-line-ends-backslash-p + (1- line-beg-pos)) (- line-beg-pos 2))))) 'after-backslash) ;; After beginning of block @@ -677,7 +681,8 @@ START is the buffer position where the sexp starts." (when (looking-at "\\.") (forward-line -1) (goto-char (line-end-position)) - (while (and (re-search-backward "\\." (line-beginning-position) t) + (while (and (re-search-backward + "\\." (line-beginning-position) t) (or (python-info-ppss-context 'comment) (python-info-ppss-context 'string) (python-info-ppss-context 'paren)))) @@ -1043,7 +1048,7 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p)) (python-info-ppss-context 'string) (python-info-ppss-context 'paren)) - (forward-line -1))))) + (forward-line -1))))) (defun python-nav-sentence-end () "Move to end of current sentence." @@ -1054,7 +1059,7 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p) (python-info-ppss-context 'string) (python-info-ppss-context 'paren)) - (forward-line 1))))) + (forward-line 1))))) (defun python-nav-backward-sentence (&optional arg) "Move backward to start of sentence. With ARG, do it arg times. @@ -1234,14 +1239,14 @@ virtualenv." (defcustom python-shell-compilation-regexp-alist `((,(rx line-start (1+ (any " \t")) "File \"" - (group (1+ (not (any "\"<")))) ; avoid `' &c - "\", line " (group (1+ digit))) + (group (1+ (not (any "\"<")))) ; avoid `' &c + "\", line " (group (1+ digit))) 1 2) (,(rx " in file " (group (1+ not-newline)) " on line " - (group (1+ digit))) + (group (1+ digit))) 1 2) (,(rx line-start "> " (group (1+ (not (any "(\"<")))) - "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") 1 2)) "`compilation-error-regexp-alist' for inferior Python." :type '(alist string) @@ -1684,18 +1689,19 @@ completions on the current context." "Do completion at point for PROCESS." (with-syntax-table python-dotty-syntax-table (let* ((line (substring-no-properties - (buffer-substring (point-at-bol) (point)) nil nil)) - (input (substring-no-properties - (or (comint-word (current-word)) "") nil nil)) + (buffer-substring (point-at-bol) (point)) nil nil)) + (input (substring-no-properties + (or (comint-word (current-word)) "") nil nil)) (prompt (buffer-substring-no-properties (overlay-start comint-last-prompt-overlay) (overlay-end comint-last-prompt-overlay))) - (completion-code + (completion-code (cond ((and (> (length python-shell-completion-pdb-string-code) 0) (string-match (concat "^" python-shell-prompt-pdb-regexp) prompt)) python-shell-completion-pdb-string-code) - ((and (> (length python-shell-completion-module-string-code) 0) + ((and (> + (length python-shell-completion-module-string-code) 0) (string-match (concat "^" python-shell-prompt-regexp) prompt) (string-match "^\\(from\\|import\\)[ \t]" line)) @@ -1708,18 +1714,18 @@ completions on the current context." (and completion-code (> (length input) 0) (python-shell-completion--get-completions line process completion-code))) - (completion (when completions - (try-completion input completions)))) + (completion (when completions + (try-completion input completions)))) (cond ((eq completion t) - (if (eq this-command last-command) - (when python-shell-completion-original-window-configuration - (set-window-configuration - python-shell-completion-original-window-configuration))) - (setq python-shell-completion-original-window-configuration nil) - t) - ((null completion) - (message "Can't find completion for \"%s\"" input) - (ding) + (if (eq this-command last-command) + (when python-shell-completion-original-window-configuration + (set-window-configuration + python-shell-completion-original-window-configuration))) + (setq python-shell-completion-original-window-configuration nil) + t) + ((null completion) + (message "Can't find completion for \"%s\"" input) + (ding) nil) ((not (string= input completion)) (progn (delete-char (- (length input))) @@ -1740,7 +1746,7 @@ completions on the current context." (and comint-last-prompt-overlay (> (point-marker) (overlay-end comint-last-prompt-overlay)) (python-shell-completion--do-completion-at-point - (get-buffer-process (current-buffer))))) + (get-buffer-process (current-buffer))))) (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. @@ -1808,13 +1814,15 @@ Argument OUTPUT is a string with the output from the comint process." ;; but in some cases (like when doing a step-in) it is ;; on the second. (when (or (looking-at python-pdbtrack-stacktrace-info-regexp) - (and (forward-line) - (looking-at python-pdbtrack-stacktrace-info-regexp))) + (and + (forward-line) + (looking-at python-pdbtrack-stacktrace-info-regexp))) (setq line-number (string-to-number (match-string-no-properties 2))) (match-string-no-properties 1))))) (if (and file-name line-number) - (let* ((tracked-buffer (python-pdbtrack-set-tracked-buffer file-name)) + (let* ((tracked-buffer + (python-pdbtrack-set-tracked-buffer file-name)) (shell-buffer (current-buffer)) (tracked-buffer-window (get-buffer-window tracked-buffer)) (tracked-buffer-line-pos)) @@ -1855,7 +1863,8 @@ inferior python process is updated properly." (error "Completion needs an inferior Python process running") (python-shell-completion--do-completion-at-point process)))) -(add-to-list 'debug-ignored-errors "^Completion needs an inferior Python process running.") +(add-to-list 'debug-ignored-errors + "^Completion needs an inferior Python process running.") ;;; Fill paragraph @@ -2031,7 +2040,7 @@ the if condition." The skeleton will be bound to python-skeleton-NAME and will be added to `python-mode-abbrev-table'." (let* ((name (symbol-name name)) - (function-name (intern (concat "python-skeleton-" name)))) + (function-name (intern (concat "python-skeleton-" name)))) `(progn (define-abbrev python-mode-abbrev-table ,name "" ',function-name) (setq python-skeleton-available @@ -2046,7 +2055,7 @@ be added to `python-mode-abbrev-table'." "Define a `python-mode' auxiliary skeleton using NAME DOC and SKEL. The skeleton will be bound to python-skeleton-NAME." (let* ((name (symbol-name name)) - (function-name (intern (concat "python-skeleton--" name))) + (function-name (intern (concat "python-skeleton--" name))) (msg (format "Add '%s' clause? " name))) (when (not skel) @@ -2107,15 +2116,15 @@ The skeleton will be bound to python-skeleton-NAME." "Function name: " "def " str " (" ("Parameter, %s: " (unless (equal ?\( (char-before)) ", ") - str) "):" \n - "\"\"\"" - "\"\"\"" \n - > _ \n) + str) "):" \n + "\"\"\"" - "\"\"\"" \n + > _ \n) (python-skeleton-define class nil "Class name: " "class " str " (" ("Inheritance, %s: " - (unless (equal ?\( (char-before)) ", ") - str) + (unless (equal ?\( (char-before)) ", ") + str) & ")" | -2 ":" \n "\"\"\"" - "\"\"\"" \n @@ -2166,7 +2175,7 @@ The skeleton will be bound to python-skeleton-NAME." (python-shell-send-string-no-output (format python-ffap-string-code module) process))) (when module-file - (substring-no-properties module-file 1 -1)))))) + (substring-no-properties module-file 1 -1)))))) (eval-after-load "ffap" '(progn @@ -2197,8 +2206,8 @@ Runs COMMAND, a shell command, as if by `compile'. See `python-check-command' for the default." (interactive (list (read-string "Check command: " - (or python-check-custom-command - (concat python-check-command " " + (or python-check-custom-command + (concat python-check-command " " (shell-quote-argument (or (let ((name (buffer-file-name))) @@ -2272,20 +2281,24 @@ will be used. If not FORCE-PROCESS is passed what (current-word) (concat current-defun "." (current-word)))))) (ppss (syntax-ppss)) - (help (when (and input - (not (string= input (concat current-defun "."))) - (not (or (python-info-ppss-context 'string ppss) - (python-info-ppss-context 'comment ppss)))) - (when (string-match (concat - (regexp-quote (concat current-defun ".")) - "self\\.") input) + (help (when (and + input + (not (string= input (concat current-defun "."))) + (not (or (python-info-ppss-context 'string ppss) + (python-info-ppss-context 'comment ppss)))) + (when (string-match + (concat + (regexp-quote (concat current-defun ".")) + "self\\.") input) (with-temp-buffer (insert input) (goto-char (point-min)) (forward-word) (forward-char) - (delete-region (point-marker) (search-forward "self.")) - (setq input (buffer-substring (point-min) (point-max))))) + (delete-region + (point-marker) (search-forward "self.")) + (setq input (buffer-substring + (point-min) (point-max))))) (python-shell-send-string-no-output (format python-eldoc-string-code input) process)))) (with-current-buffer (process-buffer process) @@ -2306,18 +2319,18 @@ inferior python process is updated properly." (defun python-eldoc-at-point (symbol) "Get help on SYMBOL using `help'. Interactively, prompt for symbol." - (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)))) - (let ((process (python-shell-get-process))) - (if (not process) - (message "Eldoc needs an inferior Python process running.") - (message (python-eldoc--get-doc-at-point symbol process))))) + (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)))) + (let ((process (python-shell-get-process))) + (if (not process) + (message "Eldoc needs an inferior Python process running.") + (message (python-eldoc--get-doc-at-point symbol process))))) ;;; Imenu @@ -2496,15 +2509,15 @@ not inside a defun." (point-marker)))))))) (defun python-info-line-ends-backslash-p (&optional line-number) - "Return non-nil if current line ends with backslash. + "Return non-nil if current line ends with backslash. With optional argument LINE-NUMBER, check that line instead." - (save-excursion - (save-restriction - (when line-number - (goto-char line-number)) - (widen) - (goto-char (line-end-position)) - (equal (char-after (1- (point))) ?\\)))) + (save-excursion + (save-restriction + (when line-number + (goto-char line-number)) + (widen) + (goto-char (line-end-position)) + (equal (char-after (1- (point))) ?\\)))) (defun python-info-continuation-line-p () "Check if current line is continuation of another. @@ -2622,8 +2635,8 @@ to \"^python-\"." (and (symbolp (car pair)) (string-match (or regexp "^python-") (symbol-name (car pair))) - (set (make-local-variable (car pair)) - (cdr pair)))) + (set (make-local-variable (car pair)) + (cdr pair)))) (buffer-local-variables from-buffer))) (defun python-util-forward-comment (&optional direction) @@ -2658,11 +2671,13 @@ if that value is non-nil." nil nil nil nil (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords))) - (set (make-local-variable 'indent-line-function) #'python-indent-line-function) + (set (make-local-variable 'indent-line-function) + #'python-indent-line-function) (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-function) + (set (make-local-variable 'fill-paragraph-function) + 'python-fill-paragraph-function) (set (make-local-variable 'beginning-of-defun-function) #'python-beginning-of-defun-function) @@ -2680,14 +2695,14 @@ if that value is non-nil." (set (make-local-variable 'skeleton-further-elements) '((abbrev-mode nil) (< '(backward-delete-char-untabify (min python-indent-offset - (current-column)))) - (^ '(- (1+ (current-indentation)))))) + (current-column)))) + (^ '(- (1+ (current-indentation)))))) (set (make-local-variable 'eldoc-documentation-function) #'python-eldoc-function) (add-to-list 'hs-special-modes-alist - `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" + `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" ,(lambda (arg) (python-end-of-defun-function)) nil)) -- cgit v1.2.1 From 27d7f16f7ab3fe862ce975c712768045b20840bc Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:35 -0300 Subject: Updated `python-font-lock-keywords' to match latest Python 2.7 and 3.2 documentation. --- lisp/progmodes/python.el | 88 +++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 34 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3dd94c3793f..51727be901a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -345,14 +345,22 @@ This variant of `rx' supports common python named REGEXPS." ;;; Font-lock and syntax - (defvar python-font-lock-keywords ;; Keywords `(,(rx symbol-start - (or "and" "del" "from" "not" "while" "as" "elif" "global" "or" "with" - "assert" "else" "if" "pass" "yield" "break" "except" "import" - "print" "class" "exec" "in" "raise" "continue" "finally" "is" - "return" "def" "for" "lambda" "try" "self") + (or + "and" "del" "from" "not" "while" "as" "elif" "global" "or" "with" + "assert" "else" "if" "pass" "yield" "break" "except" "import" "class" + "in" "raise" "continue" "finally" "is" "return" "def" "for" "lambda" + "try" + ;; Python 2: + "print" "exec" + ;; Python 3: + ;; False, None, and True are listed as keywords on the Python 3 + ;; documentation, but since they also qualify as constants they are + ;; fontified like that in order to keep font-lock consistent between + ;; Python versions. + "nonlocal") symbol-end) ;; functions (,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) @@ -362,10 +370,11 @@ This variant of `rx' supports common python named REGEXPS." (1 font-lock-type-face)) ;; Constants (,(rx symbol-start - ;; copyright, license, credits, quit, exit are added by the - ;; site module and since they are not intended to be used in - ;; programs they are not added here either. - (or "None" "True" "False" "Ellipsis" "__debug__" "NotImplemented") + (or + "Ellipsis" "False" "None" "NotImplemented" "True" "__debug__" + ;; copyright, license, credits, quit and exit are added by the site + ;; module and they are not intended to be used in programs + "copyright" "credits" "exit" "license" "quit") symbol-end) . font-lock-constant-face) ;; Decorators. (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) @@ -373,34 +382,45 @@ This variant of `rx' supports common python named REGEXPS." (1 font-lock-type-face)) ;; Builtin Exceptions (,(rx symbol-start - (or "ArithmeticError" "AssertionError" "AttributeError" - "BaseException" "BufferError" "BytesWarning" "DeprecationWarning" - "EOFError" "EnvironmentError" "Exception" "FloatingPointError" - "FutureWarning" "GeneratorExit" "IOError" "ImportError" - "ImportWarning" "IndentationError" "IndexError" "KeyError" - "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" - "NotImplementedError" "OSError" "OverflowError" - "PendingDeprecationWarning" "ReferenceError" "RuntimeError" - "RuntimeWarning" "StandardError" "StopIteration" "SyntaxError" - "SyntaxWarning" "SystemError" "SystemExit" "TabError" "TypeError" - "UnboundLocalError" "UnicodeDecodeError" "UnicodeEncodeError" - "UnicodeError" "UnicodeTranslateError" "UnicodeWarning" - "UserWarning" "ValueError" "Warning" "ZeroDivisionError") + (or + "ArithmeticError" "AssertionError" "AttributeError" "BaseException" + "DeprecationWarning" "EOFError" "EnvironmentError" "Exception" + "FloatingPointError" "FutureWarning" "GeneratorExit" "IOError" + "ImportError" "ImportWarning" "IndexError" "KeyError" + "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" + "NotImplementedError" "OSError" "OverflowError" + "PendingDeprecationWarning" "ReferenceError" "RuntimeError" + "RuntimeWarning" "StopIteration" "SyntaxError" "SyntaxWarning" + "SystemError" "SystemExit" "TypeError" "UnboundLocalError" + "UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError" + "UnicodeTranslateError" "UnicodeWarning" "UserWarning" "VMSError" + "ValueError" "Warning" "WindowsError" "ZeroDivisionError" + ;; Python 2: + "StandardError" + ;; Python 3: + "BufferError" "BytesWarning" "IndentationError" "ResourceWarning" + "TabError") symbol-end) . font-lock-type-face) ;; Builtins (,(rx symbol-start - (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" - "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" - "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" - "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" - "execfile" "file" "filter" "float" "format" "frozenset" - "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" - "int" "intern" "isinstance" "issubclass" "iter" "len" "list" - "locals" "long" "map" "max" "min" "next" "object" "oct" "open" - "ord" "pow" "print" "property" "range" "raw_input" "reduce" - "reload" "repr" "reversed" "round" "set" "setattr" "slice" - "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" - "unichr" "unicode" "vars" "xrange" "zip") + (or + "abs" "all" "any" "bin" "bool" "callable" "chr" "classmethod" + "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" + "eval" "filter" "float" "format" "frozenset" "getattr" "globals" + "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" + "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview" + "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" + "range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted" + "staticmethod" "str" "sum" "super" "tuple" "type" "vars" "zip" + "__import__" + ;; Python 2: + "basestring" "cmp" "execfile" "file" "long" "raw_input" "reduce" + "reload" "unichr" "unicode" "xrange" "apply" "buffer" "coerce" + "intern" + ;; Python 3: + "ascii" "bytearray" "bytes" "exec" + ;; Extra: + "__all__" "__doc__" "__name__" "__package__") symbol-end) . font-lock-builtin-face) ;; asignations ;; support for a = b = c = 5 -- cgit v1.2.1 From 479a14cc3a9a9a036f34f334d2bab8036932cf71 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:35 -0300 Subject: Added self to the list of font-lock-keywords --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 51727be901a..5527d851d1d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -360,7 +360,9 @@ This variant of `rx' supports common python named REGEXPS." ;; documentation, but since they also qualify as constants they are ;; fontified like that in order to keep font-lock consistent between ;; Python versions. - "nonlocal") + "nonlocal" + ;; Extra: + "self") symbol-end) ;; functions (,(rx symbol-start "def" (1+ space) (group (1+ (or word ?_)))) -- cgit v1.2.1 From dc4f2e532630fa58381b9d3e52350279c98e89b7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:36 -0300 Subject: Enhancements on indentation for lines after a backslash continuation. Backslashed continuations with parens in the middle are now handled correctly. Things like this are now indented properly: objects = Thing.objects.all() \ .filter( type="toy", status="bought" subtype="car" ) \ .aggregate( Sum('amount') ) \ .values_list() New Functions: * `python-info-beginning-of-backlash' returns the point where a backslashed line start. --- lisp/progmodes/python.el | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5527d851d1d..ff790bdc25c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -701,7 +701,12 @@ START is the buffer position where the sexp starts." ((save-excursion (back-to-indentation) (when (looking-at "\\.") - (forward-line -1) + ;; If after moving one line back point is inside a paren it + ;; needs to move back until it's not anymore + (while (prog2 + (forward-line -1) + (and (not (bobp)) + (python-info-ppss-context 'paren)))) (goto-char (line-end-position)) (while (and (re-search-backward "\\." (line-beginning-position) t) @@ -742,10 +747,12 @@ START is the buffer position where the sexp starts." (current-column)))) (t (forward-line -1) + (goto-char (python-info-beginning-of-backlash)) (if (save-excursion (and - (python-info-line-ends-backslash-p) (forward-line -1) + (goto-char + (or (python-info-beginning-of-backlash) (point))) (python-info-line-ends-backslash-p))) ;; The two previous lines ended in a backslash so we must ;; respect previous line indentation. @@ -2535,11 +2542,31 @@ not inside a defun." With optional argument LINE-NUMBER, check that line instead." (save-excursion (save-restriction + (widen) (when line-number (goto-char line-number)) + (while (and (not (eobp)) + (goto-char (line-end-position)) + (python-info-ppss-context 'paren) + (not (equal (char-before (point)) ?\\))) + (forward-line 1)) + (when (equal (char-before) ?\\) + (point-marker))))) + +(defun python-info-beginning-of-backlash (&optional line-number) + "Return the point where the backlashed line starts." + (save-excursion + (save-restriction (widen) - (goto-char (line-end-position)) - (equal (char-after (1- (point))) ?\\)))) + (when line-number + (goto-char line-number)) + (when (python-info-line-ends-backslash-p) + (while (save-excursion + (goto-char (line-beginning-position)) + (python-info-ppss-context 'paren)) + (forward-line -1)) + (back-to-indentation) + (point-marker))))) (defun python-info-continuation-line-p () "Check if current line is continuation of another. -- cgit v1.2.1 From cd1ed6c8f4a50c0bc499bdcf78bbd4e39bdfc4d2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:36 -0300 Subject: python-end-of-defun-function now works correctly when comments are not indented properly. Calling `end-of-defun' on a python file will now do the correct thing, even for cases like this: def fib(n): if n < 2: # base cases return n else: return fib(n - 2) + fib(n - 1) --- lisp/progmodes/python.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ff790bdc25c..7f4aa940d8d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1062,7 +1062,12 @@ Returns nil if point is not in a def or class." (while (and (forward-line 1) (not (eobp)) (or (not (current-word)) - (> (current-indentation) beg-defun-indent)))) + ;; This checks if the indentation is less than the base + ;; one and if the line is not a comment + (or (> (current-indentation) beg-defun-indent) + (equal + (char-after + (+ (point) (current-indentation))) ?#))))) (python-util-forward-comment) (goto-char (line-beginning-position)))) -- cgit v1.2.1 From 394f09a38f7cdb3138ed3dc778b702ab4ab3a2ba Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:36 -0300 Subject: Make `python-shell-completion--do-completion-at-point' work for calls done via `python-completion-complete-at-point'. The comint prompt retrieval logic was failing on `python-shell-completion--do-completion-at-point' when calls to it happened from outside the process buffer. --- lisp/progmodes/python.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 7f4aa940d8d..cfc15c7d040 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1726,9 +1726,11 @@ completions on the current context." (buffer-substring (point-at-bol) (point)) nil nil)) (input (substring-no-properties (or (comint-word (current-word)) "") nil nil)) - (prompt (buffer-substring-no-properties - (overlay-start comint-last-prompt-overlay) - (overlay-end comint-last-prompt-overlay))) + (prompt + (with-current-buffer (process-buffer process) + (buffer-substring-no-properties + (overlay-start comint-last-prompt-overlay) + (overlay-end comint-last-prompt-overlay)))) (completion-code (cond ((and (> (length python-shell-completion-pdb-string-code) 0) (string-match -- cgit v1.2.1 From 0d0e6ccde63ad801353ed289049940a88d626fee Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:36 -0300 Subject: Make `python-shell-completion--do-completion-at-point' to return a list for `completion-at-point' do it's job instead of replicating completion logic. Removed vars: + python-shell-completion-original-window-configuration --- lisp/progmodes/python.el | 43 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index cfc15c7d040..377ad72756f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1707,8 +1707,6 @@ and use the following as the value of this variable: :type 'string :group 'python) -(defvar python-shell-completion-original-window-configuration nil) - (defun python-shell-completion--get-completions (input process completion-code) "Retrieve available completions for INPUT using PROCESS. Argument COMPLETION-CODE is the python code used to get @@ -1722,16 +1720,21 @@ completions on the current context." (defun python-shell-completion--do-completion-at-point (process) "Do completion at point for PROCESS." (with-syntax-table python-dotty-syntax-table - (let* ((line (substring-no-properties - (buffer-substring (point-at-bol) (point)) nil nil)) - (input (substring-no-properties - (or (comint-word (current-word)) "") nil nil)) + (let* ((beg (save-excursion (skip-syntax-backward "w") (point))) + (end (point)) + (line (buffer-substring-no-properties (point-at-bol) end)) + (input (buffer-substring-no-properties beg end)) + ;; Get the last prompt for the inferior process buffer. This is + ;; used for the completion code selection heuristic. (prompt (with-current-buffer (process-buffer process) (buffer-substring-no-properties (overlay-start comint-last-prompt-overlay) (overlay-end comint-last-prompt-overlay)))) (completion-code + ;; Check wether a prompt matches a pdb string, an import statement + ;; or just the standard prompt and use the correct + ;; python-shell-completion-*-code string (cond ((and (> (length python-shell-completion-pdb-string-code) 0) (string-match (concat "^" python-shell-prompt-pdb-regexp) prompt)) @@ -1749,32 +1752,8 @@ completions on the current context." (completions (and completion-code (> (length input) 0) (python-shell-completion--get-completions - line process completion-code))) - (completion (when completions - (try-completion input completions)))) - (cond ((eq completion t) - (if (eq this-command last-command) - (when python-shell-completion-original-window-configuration - (set-window-configuration - python-shell-completion-original-window-configuration))) - (setq python-shell-completion-original-window-configuration nil) - t) - ((null completion) - (message "Can't find completion for \"%s\"" input) - (ding) - nil) - ((not (string= input completion)) - (progn (delete-char (- (length input))) - (insert completion) - t)) - (t - (unless python-shell-completion-original-window-configuration - (setq python-shell-completion-original-window-configuration - (current-window-configuration))) - (with-output-to-temp-buffer "*Python Completions*" - (display-completion-list - (all-completions input completions))) - t))))) + line process completion-code)))) + (list beg end completions)))) (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." -- cgit v1.2.1 From cb37c7e369b7cde971e865ca89e2a455bb7479da Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:37 -0300 Subject: Enhanced completion subject finding logic for `python-shell-completion--do-completion-at-point' --- lisp/progmodes/python.el | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 377ad72756f..520ed4eb29e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1720,7 +1720,24 @@ completions on the current context." (defun python-shell-completion--do-completion-at-point (process) "Do completion at point for PROCESS." (with-syntax-table python-dotty-syntax-table - (let* ((beg (save-excursion (skip-syntax-backward "w") (point))) + (let* ((beg + (save-excursion + (let* ((paren-depth (car (syntax-ppss))) + (syntax-string "w_") + (syntax-list (string-to-syntax syntax-string))) + ;; Stop scanning for the beginning of the completion subject + ;; after the char before point matches a delimiter + (while (member (car (syntax-after (1- (point)))) syntax-list) + (skip-syntax-backward syntax-string) + (when (or (equal (char-before) ?\)) + (equal (char-before) ?\")) + (forward-char -1)) + (while (or + ;; honor initial paren depth + (> (car (syntax-ppss)) paren-depth) + (python-info-ppss-context 'string)) + (forward-char -1)))) + (point))) (end (point)) (line (buffer-substring-no-properties (point-at-bol) end)) (input (buffer-substring-no-properties beg end)) @@ -1752,7 +1769,7 @@ completions on the current context." (completions (and completion-code (> (length input) 0) (python-shell-completion--get-completions - line process completion-code)))) + input process completion-code)))) (list beg end completions)))) (defun python-shell-completion-complete-at-point () -- cgit v1.2.1 From 29810afbbd2dab9e8b068650b740ed189c1e2009 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:37 -0300 Subject: Added missing dot in `python-completion-complete-at-point' error --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 520ed4eb29e..cbd6d925341 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1892,7 +1892,7 @@ inferior python process is updated properly." (interactive) (let ((process (python-shell-get-process))) (if (not process) - (error "Completion needs an inferior Python process running") + (error "Completion needs an inferior Python process running.") (python-shell-completion--do-completion-at-point process)))) (add-to-list 'debug-ignored-errors -- cgit v1.2.1 From 936bc8333f08546722a7f9ca2640488e797c39de Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:38 -0300 Subject: Enhancements on ipython 0.10 setup documentation (thanks to @tkf for the hint) --- lisp/progmodes/python.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index cbd6d925341..b87ab57c66f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -85,10 +85,12 @@ ;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") ;; For iPython 0.10 everything would be the same except for -;; `python-shell-completion-string-code': +;; `python-shell-completion-string-code' and +;; `python-shell-completion-module-string-code': ;; (setq python-shell-completion-string-code -;; "';'.join(__IP.complete('''%s'''))\n") +;; "';'.join(__IP.complete('''%s'''))\n" +;; python-shell-completion-module-string-code "") ;; Unfortunately running iPython on Windows needs some more tweaking. ;; The way you must set `python-shell-interpreter' and -- cgit v1.2.1 From 53df7818c0d296953333efceefcdde6cba51cfcf Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:38 -0300 Subject: Pass entire line of input to module completer The module completer wants e.g. 'from xxx' as input, not just 'xxx'. This change also causes all modules to be offered as completions to 'from ', whereas previously this was regarded as empty input. --- lisp/progmodes/python.el | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b87ab57c66f..b1d52926ec0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1750,24 +1750,32 @@ completions on the current context." (buffer-substring-no-properties (overlay-start comint-last-prompt-overlay) (overlay-end comint-last-prompt-overlay)))) - (completion-code + (completion-context ;; Check wether a prompt matches a pdb string, an import statement ;; or just the standard prompt and use the correct ;; python-shell-completion-*-code string (cond ((and (> (length python-shell-completion-pdb-string-code) 0) (string-match (concat "^" python-shell-prompt-pdb-regexp) prompt)) - python-shell-completion-pdb-string-code) + 'pdb) ((and (> (length python-shell-completion-module-string-code) 0) (string-match (concat "^" python-shell-prompt-regexp) prompt) (string-match "^\\(from\\|import\\)[ \t]" line)) - python-shell-completion-module-string-code) + 'import) ((string-match (concat "^" python-shell-prompt-regexp) prompt) - python-shell-completion-string-code) + 'default) (t nil))) + (completion-code + (case completion-context + ('pdb python-shell-completion-pdb-string-code) + ('import python-shell-completion-module-string-code) + ('default python-shell-completion-string-code) + (t nil))) + (input + (if (eq completion-context 'import) line input)) (completions (and completion-code (> (length input) 0) (python-shell-completion--get-completions -- cgit v1.2.1 From 799aa2afb2718db4df657fdaf1348cbfec21dd66 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:38 -0300 Subject: Use `completion-at-point` instead of `comint-dynamic-complete` in shell completion --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b1d52926ec0..6d9ca2f09d7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1799,7 +1799,7 @@ to complete." (buffer-substring (comint-line-beginning-position) (point-marker))) (indent-for-tab-command) - (comint-dynamic-complete))) + (completion-at-point))) ;;; PDB Track integration -- cgit v1.2.1 From bdfb840ec4a5ed475c28de381447f6f1e541d5d1 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:39 -0300 Subject: Deactivate mark on python-shell-send-region --- lisp/progmodes/python.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6d9ca2f09d7..a10e71dab78 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1591,8 +1591,7 @@ Returns the output. See `python-shell-send-string-no-output'." (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." (interactive "r") - (let ((deactivate-mark nil)) - (python-shell-send-string (buffer-substring start end) nil t))) + (python-shell-send-string (buffer-substring start end) nil t)) (defun python-shell-send-buffer () "Send the entire buffer to inferior Python process." -- cgit v1.2.1 From 39806de381d46f62c3bef1736a969b86015bf603 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:39 -0300 Subject: Disregard leading whitespace when forming module completions This allows an indented import line (e.g. in a function or method body) to be completed. --- lisp/progmodes/python.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a10e71dab78..59802cca623 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1761,7 +1761,7 @@ completions on the current context." (length python-shell-completion-module-string-code) 0) (string-match (concat "^" python-shell-prompt-regexp) prompt) - (string-match "^\\(from\\|import\\)[ \t]" line)) + (string-match "^[ \t]*\\(from\\|import\\)[ \t]" line)) 'import) ((string-match (concat "^" python-shell-prompt-regexp) prompt) @@ -1774,7 +1774,9 @@ completions on the current context." ('default python-shell-completion-string-code) (t nil))) (input - (if (eq completion-context 'import) line input)) + (if (eq completion-context 'import) + (replace-regexp-in-string "^[ \t]+" "" line) + input)) (completions (and completion-code (> (length input) 0) (python-shell-completion--get-completions -- cgit v1.2.1 From 6da55e5931e899acb1b7da2206967aa56da3ca0f Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:39 -0300 Subject: Don't send "if __name__ == '__main__':" to interpreter --- lisp/progmodes/python.el | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 59802cca623..16a3befc3f2 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -320,6 +320,10 @@ `(decorator . ,(rx line-start (* space) ?@ (any letter ?_) (* (any word ?_)))) `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) + `(if-name-main . ,(rx line-start "if" (+ space) "__name__" + (+ space) "==" (+ space) + (any ?' ?\") "__main__" (any ?' ?\") + (* space) ?:)) `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) `(open-paren . ,(rx (or "{" "[" "("))) `(close-paren . ,(rx (or "}" "]" ")"))) @@ -1593,12 +1597,21 @@ Returns the output. See `python-shell-send-string-no-output'." (interactive "r") (python-shell-send-string (buffer-substring start end) nil t)) -(defun python-shell-send-buffer () - "Send the entire buffer to inferior Python process." - (interactive) +(defun python-shell-send-buffer (&optional arg) + "Send the entire buffer to inferior Python process. + +With prefix arg include lines protected by \"if __name__ == '__main__':\"" + (interactive "P") (save-restriction (widen) - (python-shell-send-region (point-min) (point-max)))) + (python-shell-send-region + (point-min) + (or (and + (not arg) + (save-excursion + (re-search-forward (python-rx if-name-main) nil t)) + (match-beginning 0)) + (point-max))))) (defun python-shell-send-defun (arg) "Send the current defun to inferior Python process. -- cgit v1.2.1 From b4b661d885a9fd771f5b165142bc89c6c9f243bf Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:40 -0300 Subject: Customizable variable to control syntax highlighting in shell. Default is on. --- lisp/progmodes/python.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 16a3befc3f2..75b9f0c087e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1223,6 +1223,13 @@ It should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) +(defcustom python-shell-enable-syntax-highlighting t + "Should syntax highlighting be enabled in the python shell buffer? +Restart the python shell after changing this variable for it to take effect." + :type 'boolean + :group 'python + :safe 'booleanp) + (defcustom python-shell-send-setup-max-wait 5 "Seconds to wait for process output before code setup. If output is received before the especified time then control is @@ -1382,6 +1389,7 @@ controls which Python interpreter is run. Variables `python-shell-prompt-regexp', `python-shell-prompt-output-regexp', `python-shell-prompt-block-regexp', +`python-shell-enable-syntax-highlighting', `python-shell-completion-setup-code', `python-shell-completion-string-code', `python-shell-completion-module-string-code', @@ -1415,6 +1423,12 @@ variable. 'python-shell-completion-complete-at-point) (define-key inferior-python-mode-map (kbd "") 'python-shell-completion-complete-or-indent) + (when python-shell-enable-syntax-highlighting + (set + (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords + nil nil nil nil + (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords)))) (compilation-shell-minor-mode 1)) (defun python-shell-make-comint (cmd proc-name &optional pop) -- cgit v1.2.1 From 1dae378f104041ce85135353038b749cf806c244 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:40 -0300 Subject: Small fix in `python-shell-send-defun' (#69 thanks mafm-git!) --- lisp/progmodes/python.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 75b9f0c087e..4671d9f0f1a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1635,10 +1635,12 @@ When argument ARG is non-nil sends the innermost defun." (python-shell-send-region (progn (or (python-beginning-of-defun-function) - (progn (beginning-of-line) (point-marker)))) + (beginning-of-line)) + (point-marker)) (progn (or (python-end-of-defun-function) - (progn (end-of-line) (point-marker))))))) + (end-of-line)) + (point-marker))))) (defun python-shell-send-file (file-name &optional process temp-file-name) "Send FILE-NAME to inferior Python PROCESS. -- cgit v1.2.1 From 8f1eba8b5e51ddaf4f9779271be20a8a75b4d7a0 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Thu, 17 May 2012 00:03:41 -0300 Subject: Treat empty triple quote Fix #70 --- lisp/progmodes/python.el | 1 + 1 file changed, 1 insertion(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4671d9f0f1a..7cfbb34aea3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -465,6 +465,7 @@ This variant of `rx' supports common python named REGEXPS." ;; string delimiters. Fixme: Is there a better way? ;; First avoid a sequence preceded by an odd number of backslashes. `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + "\\(?:''''''\\|\"\"\"\"\"\"\\)" ; Empty triple-quote "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") (3 (python-quote-syntax))))) -- cgit v1.2.1 From 035c45e336a3ef831bfdb3bbfcfa1bb925c9783b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:41 -0300 Subject: Do not call `switch-to-buffer' in `python-shell-get-or-create-process' For this to work properly `run-python' now calls `python-shell-make-comint' with the POP argument set to nil. --- lisp/progmodes/python.el | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 7cfbb34aea3..1b98a046b6a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1473,7 +1473,7 @@ run). (y-or-n-p "Make dedicated process? ") (read-string "Run Python: " (python-shell-parse-command))) (list nil (python-shell-parse-command)))) - (python-shell-make-comint cmd (python-shell-get-process-name dedicated) t) + (python-shell-make-comint cmd (python-shell-get-process-name dedicated)) dedicated) (defun run-python-internal () @@ -1511,8 +1511,7 @@ of commands.)" (defun python-shell-get-or-create-process () "Get or create an inferior Python process for current buffer and return it." - (let* ((old-buffer (current-buffer)) - (dedicated-proc-name (python-shell-get-process-name t)) + (let* ((dedicated-proc-name (python-shell-get-process-name t)) (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name)) (global-proc-name (python-shell-get-process-name nil)) (global-proc-buffer-name (format "*%s*" global-proc-name)) @@ -1524,7 +1523,6 @@ of commands.)" (setq dedicated-running t) (setq global-running t))) ;; Always prefer dedicated - (switch-to-buffer old-buffer) (get-buffer-process (if dedicated-running dedicated-proc-buffer-name global-proc-buffer-name)))) -- cgit v1.2.1 From 90a41b9dc2f64c4cd087ca4651dbf6066617fb4f Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:41 -0300 Subject: Removed tabs and fixed indentation in several parts. --- lisp/progmodes/python.el | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1b98a046b6a..3eed67ab3a4 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -321,9 +321,9 @@ (* (any word ?_)))) `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) `(if-name-main . ,(rx line-start "if" (+ space) "__name__" - (+ space) "==" (+ space) - (any ?' ?\") "__main__" (any ?' ?\") - (* space) ?:)) + (+ space) "==" (+ space) + (any ?' ?\") "__main__" (any ?' ?\") + (* space) ?:)) `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) `(open-paren . ,(rx (or "{" "[" "("))) `(close-paren . ,(rx (or "}" "]" ")"))) @@ -1620,11 +1620,11 @@ With prefix arg include lines protected by \"if __name__ == '__main__':\"" (python-shell-send-region (point-min) (or (and - (not arg) - (save-excursion - (re-search-forward (python-rx if-name-main) nil t)) - (match-beginning 0)) - (point-max))))) + (not arg) + (save-excursion + (re-search-forward (python-rx if-name-main) nil t)) + (match-beginning 0)) + (point-max))))) (defun python-shell-send-defun (arg) "Send the current defun to inferior Python process. @@ -1795,16 +1795,16 @@ completions on the current context." (concat "^" python-shell-prompt-regexp) prompt) 'default) (t nil))) - (completion-code - (case completion-context - ('pdb python-shell-completion-pdb-string-code) - ('import python-shell-completion-module-string-code) - ('default python-shell-completion-string-code) - (t nil))) - (input - (if (eq completion-context 'import) - (replace-regexp-in-string "^[ \t]+" "" line) - input)) + (completion-code + (case completion-context + ('pdb python-shell-completion-pdb-string-code) + ('import python-shell-completion-module-string-code) + ('default python-shell-completion-string-code) + (t nil))) + (input + (if (eq completion-context 'import) + (replace-regexp-in-string "^[ \t]+" "" line) + input)) (completions (and completion-code (> (length input) 0) (python-shell-completion--get-completions -- cgit v1.2.1 From 5eae76aed18a165854f55911ab8a5ac2bd83feed Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:42 -0300 Subject: Readjust indentation automatically on lines with closing parens. For this to work `python-indent-post-self-insert-function' is added to the `post-self-insert-hook.' when python-mode is initialized. New functions: + `python-indent-post-self-insert-function' --- lisp/progmodes/python.el | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3eed67ab3a4..47e6fc0380d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -990,6 +990,27 @@ With numeric ARG, just insert that many colons. With (indent-line-to indentation))))))) (put 'python-indent-electric-colon 'delete-selection t) +(defun python-indent-post-self-insert-function () + "Adjust closing paren line indentation after a char is added. +This function is intended to be added to the +`post-self-insert-hook.' If a line renders a paren alone, after +adding a char before it, the line will be re-indented +automatically if needed." + (when (and (eq (char-before) last-command-event) + (= (current-indentation) 0) + (not (bolp)) + (memq (char-after) '(?\) ?\] ?\}))) + (let ((indentation (save-excursion + ;; If after going to the beginning of line the point + ;; is still inside a paren it's ok to do the trick + (goto-char (line-beginning-position)) + (when (python-info-ppss-context 'paren) + (python-indent-calculate-indentation))))) + (when (and indentation + (< (current-indentation) indentation)) + (save-excursion + (indent-line-to indentation)))))) + ;;; Navigation @@ -2778,6 +2799,9 @@ if that value is non-nil." (add-hook 'completion-at-point-functions 'python-completion-complete-at-point nil 'local) + (add-hook 'post-self-insert-hook + 'python-indent-post-self-insert-function nil 'local) + (setq imenu-create-index-function #'python-imenu-create-index) (set (make-local-variable 'add-log-current-defun-function) -- cgit v1.2.1 From cd7ab092997474f7abba8bff2e0c5df888011ed7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:42 -0300 Subject: Enhanced closing block notification when line is indented or a colon is inserted. `python-indent-line' and `python-indent-electric-colon' now uses the new `python-info-closing-block-message' function that takes care of messaging the block the current line is closing (if applicable). New Functions: + `python-info-closing-block-message' --- lisp/progmodes/python.el | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 47e6fc0380d..e549c477233 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -869,15 +869,7 @@ to (`nth' `python-indent-current-level' `python-indent-levels')" (beginning-of-line) (delete-horizontal-space) (indent-to (nth python-indent-current-level python-indent-levels)) - (save-restriction - (widen) - (let ((closing-block-point (python-info-closing-block))) - (when closing-block-point - (message "Closes %s" (buffer-substring - closing-block-point - (save-excursion - (goto-char closing-block-point) - (line-end-position)))))))) + (python-info-closing-block-message)) (defun python-indent-line-function () "`indent-line-function' for Python mode. @@ -983,10 +975,11 @@ With numeric ARG, just insert that many colons. With (python-info-ppss-context 'comment)))) (let ((indentation (current-indentation)) (calculated-indentation (python-indent-calculate-indentation))) + (python-info-closing-block-message) (when (> indentation calculated-indentation) (save-excursion (indent-line-to calculated-indentation) - (when (not (python-info-closing-block)) + (when (not (python-info-closing-block-message)) (indent-line-to indentation))))))) (put 'python-indent-electric-colon 'delete-selection t) @@ -2600,6 +2593,20 @@ not inside a defun." (when (member (current-word) '("except" "else")) (point-marker)))))))) +(defun python-info-closing-block-message (&optional closing-block-point) + "Message the contents of the block the current line closes. +With optional argument CLOSING-BLOCK-POINT use that instead of +recalculating it calling `python-info-closing-block'." + (let ((point (or closing-block-point (python-info-closing-block)))) + (when point + (save-restriction + (widen) + (message "Closes %s" (save-excursion + (goto-char point) + (back-to-indentation) + (buffer-substring + (point) (line-end-position)))))))) + (defun python-info-line-ends-backslash-p (&optional line-number) "Return non-nil if current line ends with backslash. With optional argument LINE-NUMBER, check that line instead." -- cgit v1.2.1 From cd05d2a6b0a1b94ae88b0f1acc45bb857d0c9e86 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:42 -0300 Subject: Enhancements to `python-indent-post-self-insert-function' Fixed case where the paren base indentation is not 0. --- lisp/progmodes/python.el | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e549c477233..4e9c045d184 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -990,19 +990,16 @@ This function is intended to be added to the adding a char before it, the line will be re-indented automatically if needed." (when (and (eq (char-before) last-command-event) - (= (current-indentation) 0) (not (bolp)) (memq (char-after) '(?\) ?\] ?\}))) - (let ((indentation (save-excursion - ;; If after going to the beginning of line the point - ;; is still inside a paren it's ok to do the trick - (goto-char (line-beginning-position)) - (when (python-info-ppss-context 'paren) - (python-indent-calculate-indentation))))) - (when (and indentation - (< (current-indentation) indentation)) - (save-excursion - (indent-line-to indentation)))))) + (save-excursion + (goto-char (line-beginning-position)) + ;; If after going to the beginning of line the point + ;; is still inside a paren it's ok to do the trick + (when (python-info-ppss-context 'paren) + (let ((indentation (python-indent-calculate-indentation))) + (when (< (current-indentation) indentation) + (indent-line-to indentation))))))) ;;; Navigation -- cgit v1.2.1 From a7a6d8ff0ecb3059225ed1eb4d61e941d2afeb4c Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Thu, 17 May 2012 00:03:42 -0300 Subject: Variable name change python-shell-enable-syntax-highlighting -> python-shell-enable-font-lock --- lisp/progmodes/python.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4e9c045d184..3c1e85834de 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1235,7 +1235,7 @@ It should not contain a caret (^) at the beginning." :group 'python :safe 'stringp) -(defcustom python-shell-enable-syntax-highlighting t +(defcustom python-shell-enable-font-lock t "Should syntax highlighting be enabled in the python shell buffer? Restart the python shell after changing this variable for it to take effect." :type 'boolean @@ -1401,7 +1401,7 @@ controls which Python interpreter is run. Variables `python-shell-prompt-regexp', `python-shell-prompt-output-regexp', `python-shell-prompt-block-regexp', -`python-shell-enable-syntax-highlighting', +`python-shell-enable-font-lock', `python-shell-completion-setup-code', `python-shell-completion-string-code', `python-shell-completion-module-string-code', @@ -1435,7 +1435,7 @@ variable. 'python-shell-completion-complete-at-point) (define-key inferior-python-mode-map (kbd "") 'python-shell-completion-complete-or-indent) - (when python-shell-enable-syntax-highlighting + (when python-shell-enable-font-lock (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords -- cgit v1.2.1 From 0d85f46527ddbe40510181c025a67bd819d208b3 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:43 -0300 Subject: Enhancements to `python-end-of-defun-function' so it's not blocked by top level assignments (fixes #64) --- lisp/progmodes/python.el | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3c1e85834de..71c5453ee2d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1080,12 +1080,9 @@ Returns nil if point is not in a def or class." (while (and (forward-line 1) (not (eobp)) (or (not (current-word)) - ;; This checks if the indentation is less than the base - ;; one and if the line is not a comment - (or (> (current-indentation) beg-defun-indent) - (equal - (char-after - (+ (point) (current-indentation))) ?#))))) + (equal (char-after (+ (point) (current-indentation))) ?#) + (> (current-indentation) beg-defun-indent) + (not (looking-at python-nav-beginning-of-defun-regexp))))) (python-util-forward-comment) (goto-char (line-beginning-position)))) -- cgit v1.2.1 From b15e880041f5aacb12de0959c79d60e6065188fd Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:43 -0300 Subject: `python-nav-list-defun-positions' now caches defuns positions (#75) This is a simplified version of @dandavison pull request (thanks dan!) `python-nav-list-defun-positions' now uses `python-nav-list-defun-positions-cache' buffer local variable to store cached values of defun positions. `python-nav-jump-to-defun' now benefits from this new cache and if called with prefix argument it will invalidate it so new defuns are scanned. New Vars: + `python-nav-list-defun-positions-cache' --- lisp/progmodes/python.el | 55 +++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 71c5453ee2d..4654143a160 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1134,30 +1134,39 @@ With negative argument, move backward repeatedly to start of sentence." (forward-line -1) (setq arg (1+ arg)))) -(defun python-nav-list-defun-positions (&optional include-type) +(defvar python-nav-list-defun-positions-cache nil) +(make-variable-buffer-local 'python-nav-list-defun-positions-cache) + +(defun python-nav-list-defun-positions (&optional include-type rescan) "Make an Alist of defun names and point markers for current buffer. When optional argument INCLUDE-TYPE is non-nil the type is -included the defun name." - (let ((defs)) - (save-restriction - (widen) - (save-excursion - (goto-char (point-max)) - (while (re-search-backward python-nav-beginning-of-defun-regexp nil t) - (when (and (not (python-info-ppss-context 'string)) - (not (python-info-ppss-context 'comment)) - (not (python-info-ppss-context 'parent))) - (add-to-list - 'defs (cons - (python-info-current-defun include-type) - (point-marker))))) - defs)))) - -(defun python-nav-read-defun () +included the defun name. With optional argument RESCAN the +`python-nav-list-defun-positions-cache' is invalidated and the +list of defun is regenerated again." + (if (and python-nav-list-defun-positions-cache (not rescan)) + python-nav-list-defun-positions-cache + (let ((defs)) + (save-restriction + (widen) + (save-excursion + (goto-char (point-max)) + (while (re-search-backward python-nav-beginning-of-defun-regexp nil t) + (when (and (not (python-info-ppss-context 'string)) + (not (python-info-ppss-context 'comment)) + (not (python-info-ppss-context 'parent))) + (add-to-list + 'defs (cons + (python-info-current-defun include-type) + (point-marker))))) + (setq python-nav-list-defun-positions-cache defs)))))) + +(defun python-nav-read-defun (&optional rescan) "Read a defun name of current buffer and return its point marker. A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned -when defun is completed, else nil." - (let ((defs (python-nav-list-defun-positions))) +when defun is completed, else nil. With optional argument RESCAN +forces `python-nav-list-defun-positions' to invalidate its +cache." + (let ((defs (python-nav-list-defun-positions nil rescan))) (minibuffer-with-setup-hook (lambda () (setq minibuffer-completion-table (mapcar 'car defs))) @@ -1169,9 +1178,11 @@ when defun is completed, else nil." (assoc-string stringdef defs)))))) (defun python-nav-jump-to-defun (def) - "Jump to the definition of DEF in current file." + "Jump to the definition of DEF in current file. +Locations are cached; use a C-u prefix argument to force a +rescan." (interactive - (list (python-nav-read-defun))) + (list (python-nav-read-defun current-prefix-arg))) (when (not (called-interactively-p 'interactive)) (setq def (assoc-string def (python-nav-list-defun-positions)))) (let ((def-marker (cdr def))) -- cgit v1.2.1 From 462973b5ef2bdb31392ec8a87ea23d5501059db2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:43 -0300 Subject: Enhancements on `python-beginning-of-defun-function' for `mark-defun' behavior. `mark-defun' was failing when point was right at the start of a defun. --- lisp/progmodes/python.el | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4654143a160..1279c38b68b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1052,16 +1052,19 @@ negative do the same but backwards. When NODECORATORS is non-nil decorators are not included. Return non-nil if point is moved to the `beginning-of-defun'." (when (or (null arg) (= arg 0)) (setq arg 1)) - (if (> arg 0) - (dotimes (i arg (python-nav-beginning-of-defun nodecorators))) - (let ((found)) - (dotimes (i (- arg) found) - (python-end-of-defun-function) - (python-util-forward-comment) - (goto-char (line-end-position)) - (when (not (eobp)) - (setq found - (python-nav-beginning-of-defun nodecorators))))))) + (cond ((and (eq this-command 'mark-defun) + (looking-at python-nav-beginning-of-defun-regexp))) + ((> arg 0) + (dotimes (i arg (python-nav-beginning-of-defun nodecorators)))) + (t + (let ((found)) + (dotimes (i (- arg) found) + (python-end-of-defun-function) + (python-util-forward-comment) + (goto-char (line-end-position)) + (when (not (eobp)) + (setq found + (python-nav-beginning-of-defun nodecorators)))))))) (defun python-end-of-defun-function () "Move point to the end of def or class. -- cgit v1.2.1 From 8c6f9e60dfec606a8f00101c41bf6988496cb86a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:44 -0300 Subject: New defun navigation machinery `python-nav-beginning-of-defun' and `python-beginning-of-defun-function' have changed and now they don't handle decorators anymore. `python-end-of-defun-function' is now smart enough to detect the real ending of the current defun. `python-shell-send-defun' always sends the outermost defun and includes decorators. When called with prefix argument the decorators are skipped. `python-info-current-defun' has been modified to use the new defun movement API. New Functions: + python-info-looking-at-beginning-of-defun --- lisp/progmodes/python.el | 170 +++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 78 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1279c38b68b..3b0b418899c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1010,84 +1010,86 @@ automatically if needed." The name of the defun should be grouped so it can be retrieved via `match-string'.") -(defun python-nav-beginning-of-defun (&optional nodecorators) +(defun python-nav-beginning-of-defun (&optional arg) "Move point to `beginning-of-defun'. -When NODECORATORS is non-nil decorators are not included. This -is the main part of`python-beginning-of-defun-function' -implementation. Return non-nil if point is moved to the -`beginning-of-defun'." - (let ((indent-pos (save-excursion - (back-to-indentation) - (point-marker))) - (found) - (include-decorators - (lambda () - (when (not nodecorators) - (when (save-excursion - (forward-line -1) - (looking-at (python-rx decorator))) - (while (and (not (bobp)) - (forward-line -1) - (looking-at (python-rx decorator)))) - (when (not (bobp)) (forward-line 1))))))) - (if (and (> (point) indent-pos) - (save-excursion - (goto-char (line-beginning-position)) - (looking-at python-nav-beginning-of-defun-regexp))) - (progn - (goto-char (line-beginning-position)) - (funcall include-decorators) - (setq found t)) - (goto-char (line-beginning-position)) - (when (re-search-backward python-nav-beginning-of-defun-regexp nil t) - (setq found t)) - (goto-char (or (python-info-ppss-context 'string) (point))) - (funcall include-decorators)) - found)) - -(defun python-beginning-of-defun-function (&optional arg nodecorators) +With positive ARG move search backwards. With negative do the +same but forward. When ARG is nil or 0 defaults to 1. This is +the main part of `python-beginning-of-defun-function'. Return +non-nil if point is moved to `beginning-of-defun'." + (when (or (null arg) (= arg 0)) (setq arg 1)) + (let* ((re-search-fn (if (> arg 0) + #'re-search-backward + #'re-search-forward)) + (line-beg-pos (line-beginning-position)) + (line-content-start (+ line-beg-pos (current-indentation))) + (pos (point-marker)) + (found + (progn + (when (and (< arg 0) + (python-info-looking-at-beginning-of-defun)) + (end-of-line 1)) + (while (and (funcall re-search-fn + python-nav-beginning-of-defun-regexp nil t) + (python-info-ppss-context-type))) + (and (python-info-looking-at-beginning-of-defun) + (or (not (= (line-number-at-pos pos) + (line-number-at-pos))) + (and (>= (point) line-beg-pos) + (<= (point) line-content-start) + (> pos line-content-start))))))) + (if found + (or (beginning-of-line 1) t) + (and (goto-char pos) nil)))) + +(defun python-beginning-of-defun-function (&optional arg) "Move point to the beginning of def or class. -With positive ARG move that number of functions forward. With -negative do the same but backwards. When NODECORATORS is non-nil -decorators are not included. Return non-nil if point is moved to the -`beginning-of-defun'." +With positive ARG move that number of functions backwards. With +negative do the same but forward. When ARG is nil or 0 defaults +to 1. Return non-nil if point is moved to `beginning-of-defun'." (when (or (null arg) (= arg 0)) (setq arg 1)) - (cond ((and (eq this-command 'mark-defun) - (looking-at python-nav-beginning-of-defun-regexp))) - ((> arg 0) - (dotimes (i arg (python-nav-beginning-of-defun nodecorators)))) - (t - (let ((found)) - (dotimes (i (- arg) found) - (python-end-of-defun-function) - (python-util-forward-comment) - (goto-char (line-end-position)) - (when (not (eobp)) - (setq found - (python-nav-beginning-of-defun nodecorators)))))))) + (let ((found)) + (cond ((and (eq this-command 'mark-defun) + (python-info-looking-at-beginning-of-defun))) + (t + (dotimes (i (if (> arg 0) arg (- arg))) + (when (and (python-nav-beginning-of-defun arg) + (not found)) + (setq found t))))) + found)) (defun python-end-of-defun-function () "Move point to the end of def or class. Returns nil if point is not in a def or class." (interactive) - (let ((beg-defun-indent) - (decorator-regexp "[[:space:]]*@")) - (when (looking-at decorator-regexp) - (while (and (not (eobp)) - (forward-line 1) - (looking-at decorator-regexp)))) - (when (not (looking-at python-nav-beginning-of-defun-regexp)) - (python-beginning-of-defun-function)) - (setq beg-defun-indent (current-indentation)) - (forward-line 1) - (while (and (forward-line 1) - (not (eobp)) - (or (not (current-word)) - (equal (char-after (+ (point) (current-indentation))) ?#) - (> (current-indentation) beg-defun-indent) - (not (looking-at python-nav-beginning-of-defun-regexp))))) - (python-util-forward-comment) - (goto-char (line-beginning-position)))) + (let ((beg-defun-indent)) + (when (or (python-info-looking-at-beginning-of-defun) + (python-beginning-of-defun-function 1) + (python-beginning-of-defun-function -1)) + (setq beg-defun-indent (current-indentation)) + (forward-line 1) + ;; Go as forward as possible + (while (and (or + (python-nav-beginning-of-defun -1) + (and (goto-char (point-max)) nil)) + (> (current-indentation) beg-defun-indent))) + (beginning-of-line 1) + ;; Go as backwards as possible + (while (and (forward-line -1) + (not (bobp)) + (or (not (current-word)) + (equal (char-after (+ (point) (current-indentation))) ?#) + (<= (current-indentation) beg-defun-indent) + (looking-at (python-rx decorator)) + (python-info-ppss-context-type)))) + (forward-line 1) + ;; If point falls inside a paren or string context the point is + ;; forwarded at the end of it (or end of buffer if its not closed) + (let ((context-type (python-info-ppss-context-type))) + (when (memq context-type '(paren string)) + ;; Slow but safe. + (while (and (not (eobp)) + (python-info-ppss-context-type)) + (forward-line 1))))))) (defun python-nav-sentence-start () "Move to start of current sentence." @@ -1166,7 +1168,7 @@ list of defun is regenerated again." (defun python-nav-read-defun (&optional rescan) "Read a defun name of current buffer and return its point marker. A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned -when defun is completed, else nil. With optional argument RESCAN +when defun is completed, else nil. With optional argument RESCAN forces `python-nav-list-defun-positions' to invalidate its cache." (let ((defs (python-nav-list-defun-positions nil rescan))) @@ -1650,17 +1652,23 @@ With prefix arg include lines protected by \"if __name__ == '__main__':\"" (defun python-shell-send-defun (arg) "Send the current defun to inferior Python process. -When argument ARG is non-nil sends the innermost defun." +When argument ARG is non-nil do not include decorators." (interactive "P") (save-excursion (python-shell-send-region (progn - (or (python-beginning-of-defun-function) - (beginning-of-line)) + (end-of-line 1) + (while (and (or (python-beginning-of-defun-function) + (beginning-of-line 1)) + (> (current-indentation) 0))) + (when (not arg) + (while (and (forward-line -1) + (looking-at (python-rx decorator)))) + (forward-line 1)) (point-marker)) (progn (or (python-end-of-defun-function) - (end-of-line)) + (end-of-line 1)) (point-marker))))) (defun python-shell-send-file (file-name &optional process temp-file-name) @@ -2553,10 +2561,9 @@ not inside a defun." (save-restriction (widen) (save-excursion - (goto-char (line-end-position)) - (python-util-forward-comment -1) + (end-of-line 1) (setq min-indent (current-indentation)) - (while (python-beginning-of-defun-function 1 t) + (while (python-beginning-of-defun-function 1) (when (or (< (current-indentation) min-indent) first-run) (setq first-run nil) @@ -2742,6 +2749,13 @@ The type returned can be 'comment, 'string or 'paren." 'paren) (t nil)))) +(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) + "Return nil of point is at `beginning-of-defun'." + (and (not (python-info-ppss-context-type)) + (save-excursion + (beginning-of-line 1) + (looking-at python-nav-beginning-of-defun-regexp)))) + ;;; Utility functions -- cgit v1.2.1 From 4289485a58810ee957c4151f8795b5fe717f264a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:44 -0300 Subject: Function documentation enhancements --- lisp/progmodes/python.el | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3b0b418899c..1ede66058e8 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1184,7 +1184,7 @@ cache." (defun python-nav-jump-to-defun (def) "Jump to the definition of DEF in current file. -Locations are cached; use a C-u prefix argument to force a +Locations are cached; use a `C-u' prefix argument to force a rescan." (interactive (list (python-nav-read-defun current-prefix-arg))) @@ -1637,7 +1637,7 @@ Returns the output. See `python-shell-send-string-no-output'." (defun python-shell-send-buffer (&optional arg) "Send the entire buffer to inferior Python process. -With prefix arg include lines protected by \"if __name__ == '__main__':\"" +With prefix ARG include lines surrounded by \"if __name__ == '__main__':\"" (interactive "P") (save-restriction (widen) @@ -1961,7 +1961,7 @@ inferior python process is updated properly." (interactive) (let ((process (python-shell-get-process))) (if (not process) - (error "Completion needs an inferior Python process running.") + (error "Completion needs an inferior Python process running") (python-shell-completion--do-completion-at-point process)))) (add-to-list 'debug-ignored-errors @@ -2639,7 +2639,8 @@ With optional argument LINE-NUMBER, check that line instead." (point-marker))))) (defun python-info-beginning-of-backlash (&optional line-number) - "Return the point where the backlashed line starts." + "Return the point where the backlashed line start. +Optional argument LINE-NUMBER forces the line number to check against." (save-excursion (save-restriction (widen) @@ -2750,8 +2751,8 @@ The type returned can be 'comment, 'string or 'paren." (t nil)))) (defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) - "Return nil of point is at `beginning-of-defun'." - (and (not (python-info-ppss-context-type)) + "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." + (and (not (python-info-ppss-context-type (or syntax-ppss (syntax-ppss)))) (save-excursion (beginning-of-line 1) (looking-at python-nav-beginning-of-defun-regexp)))) @@ -2781,7 +2782,8 @@ to \"^python-\"." (buffer-local-variables from-buffer))) (defun python-util-forward-comment (&optional direction) - "Python mode specific version of `forward-comment'." + "Python mode specific version of `forward-comment'. +Optional argument DIRECTION defines the direction to move to." (let ((comment-start (python-info-ppss-context 'comment)) (factor (if (< (or direction 0) 0) -99999 -- cgit v1.2.1 From c6d3df36da36ee383a6dcde5cce76175258ba269 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:45 -0300 Subject: Make `which-func-functions' defaults to '(python-info-current-defun) --- lisp/progmodes/python.el | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1ede66058e8..4b9a29845c5 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2838,6 +2838,8 @@ if that value is non-nil." (set (make-local-variable 'add-log-current-defun-function) #'python-info-current-defun) + (add-hook 'which-func-functions #'python-info-current-defun nil t) + (set (make-local-variable 'skeleton-further-elements) '((abbrev-mode nil) (< '(backward-delete-char-untabify (min python-indent-offset -- cgit v1.2.1 From bd982677367c3d441cea1d1c60cb3ad0bc3f263b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:45 -0300 Subject: Revert "Merge pull request #83 from tkf/empty-triple-quote" This reverts commit f4d086427343eb0b8bc91355388d2437c568b1f6, reversing changes made to 8f697fd2ef23a9e1d9a06ac8ca419d76ccedb27d. While the fix worked with empty triple quoted strings it broke simpler cases like these: """ def someting(a, b, c): " """ or ''' def someting(a, b, c): ' ''' Another approach is needed for giving triple quoted string the correct syntax. --- lisp/progmodes/python.el | 1 - 1 file changed, 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4b9a29845c5..2da5337b8c5 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -465,7 +465,6 @@ This variant of `rx' supports common python named REGEXPS." ;; string delimiters. Fixme: Is there a better way? ;; First avoid a sequence preceded by an odd number of backslashes. `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. - "\\(?:''''''\\|\"\"\"\"\"\"\\)" ; Empty triple-quote "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") (3 (python-quote-syntax))))) -- cgit v1.2.1 From 09268a54ae6f690d0fcfbf8aeb02710b14248d6b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:45 -0300 Subject: Copyright information update --- lisp/progmodes/python.el | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2da5337b8c5..c41d99bdf81 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,28 +1,28 @@ ;;; python.el --- Python's flying circus support for Emacs -;; Copyright (C) 2010, 2011 Free Software Foundation, Inc. +;; Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc. ;; Author: FabiĆ”n E. Gallina ;; URL: https://github.com/fgallina/python.el -;; Version: 0.23.1 +;; Version: 0.24.2 ;; Maintainer: FSF ;; Created: Jul 2010 ;; Keywords: languages -;; This file is NOT part of GNU Emacs. +;; This file is part of GNU Emacs. -;; python.el 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 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. -;; python.el 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. +;; 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 python.el. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -30,10 +30,6 @@ ;; indentation bits extracted from original Dave Love's python.el ;; found in GNU/Emacs. -;; While it probably has less features than Dave Love's python.el and -;; PSF's python-mode.el it provides the main stuff you'll need while -;; keeping it simple :) - ;; Implements Syntax highlighting, Indentation, Movement, Shell ;; interaction, Shell completion, Shell virtualenv support, Pdb ;; tracking, Symbol completion, Skeletons, FFAP, Code Check, Eldoc, -- cgit v1.2.1 From aeadd9a4699f7606bb2ff5fe04a927cf68e5e211 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 17 May 2012 00:03:46 -0300 Subject: Migrated python-font-lock-syntactic-keywords to python-syntax-propertize-function This is a backport of Stefan Monnier's migration to the new font-lock machinery (introduced in 2010-09-10T23:13:42Z!monnier@iro.umontreal.ca). New Vars: + python-syntax-propertize-function Deleted Vars: + python-font-lock-syntactic-keywords --- lisp/progmodes/python.el | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c41d99bdf81..4fa8dff2a72 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -456,13 +456,15 @@ This variant of `rx' supports common python named REGEXPS." (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)))) -(defconst python-font-lock-syntactic-keywords +(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. - `((,(concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. - "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") - (3 (python-quote-syntax))))) + (syntax-propertize-rules + (;; Ā”Backrefs don't work in syntax-propertize-rules! + (concat "\\(?:\\([RUru]\\)[Rr]?\\|^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + "\\(?:\\('\\)'\\('\\)\\|\\(?2:\"\\)\"\\(?3:\"\\)\\)") + (3 (ignore (python-quote-syntax)))))) (defun python-quote-syntax () "Put `syntax-table' property correctly on triple quote. @@ -1444,11 +1446,10 @@ variable. (define-key inferior-python-mode-map (kbd "") 'python-shell-completion-complete-or-indent) (when python-shell-enable-font-lock - (set - (make-local-variable 'font-lock-defaults) - '(python-font-lock-keywords - nil nil nil nil - (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords)))) + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil)) + (set (make-local-variable 'syntax-propertize-function) + python-syntax-propertize-function)) (compilation-shell-minor-mode 1)) (defun python-shell-make-comint (cmd proc-name &optional pop) @@ -2805,9 +2806,10 @@ if that value is non-nil." (set (make-local-variable 'parse-sexp-ignore-comments) t) (set (make-local-variable 'font-lock-defaults) - '(python-font-lock-keywords - nil nil nil nil - (font-lock-syntactic-keywords . python-font-lock-syntactic-keywords))) + '(python-font-lock-keywords nil nil nil nil)) + + (set (make-local-variable 'syntax-propertize-function) + python-syntax-propertize-function) (set (make-local-variable 'indent-line-function) #'python-indent-line-function) -- cgit v1.2.1 From 9ddf3c74c8c1d4eb255f374852018a31fcd21753 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 14 Jun 2012 22:33:57 -0300 Subject: * lisp/progmodes/python.el: (python-indent, python-guess-indent, python-use-skeletons): New obsolete variable aliases. (python-indent-guess-indent-offset): Make interactive. --- lisp/progmodes/python.el | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4fa8dff2a72..9bf89840e92 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -547,6 +547,12 @@ It makes underscores and dots word constituent chars.") :group 'python :safe 'booleanp) +(define-obsolete-variable-alias + 'python-indent 'python-indent-offset "24.2") + +(define-obsolete-variable-alias + 'python-guess-indent 'python-indent-guess-indent-offset "24.2") + (defvar python-indent-current-level 0 "Current indentation level `python-indent-line-function' is using.") @@ -560,6 +566,7 @@ These make `python-indent-calculate-indentation' subtract the value of (defun python-indent-guess-indent-offset () "Guess and set `python-indent-offset' for the current buffer." + (interactive) (save-excursion (save-restriction (widen) @@ -2117,6 +2124,9 @@ the if condition." :group 'python :safe 'booleanp) +(define-obsolete-variable-alias + 'python-use-skeletons 'python-skeleton-autoinsert "24.2") + (defvar python-skeleton-available '() "Internal list of available skeletons.") -- cgit v1.2.1 From 2d7b84eab6a10752713af889f291c046eed33677 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 14 Jun 2012 23:22:19 -0300 Subject: lisp/progmodes/python.el: Updated Copyright years. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 9bf89840e92..1b1f2a9c2de 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,6 +1,6 @@ ;;; python.el --- Python's flying circus support for Emacs -;; Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc. +;; Copyright (C) 2003-2012 Free Software Foundation, Inc. ;; Author: FabiĆ”n E. Gallina ;; URL: https://github.com/fgallina/python.el -- cgit v1.2.1 From 48d1354eb8e8e7dc759400a2f001d02587f15be2 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 17 Jun 2012 01:53:31 -0700 Subject: Spelling fixes. --- lisp/progmodes/python.el | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1b1f2a9c2de..dfa72a3084a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -50,7 +50,7 @@ ;; (`python-nav-forward-sentence', `python-nav-backward-sentence' ;; respectively). Extra functions `python-nav-sentence-start' and ;; `python-nav-sentence-end' are included to move to the beginning and -;; to the end of a setence while taking care of multiline definitions. +;; to the end of a sentence while taking care of multiline definitions. ;; `python-nav-jump-to-defun' is provided and allows jumping to a ;; function or class definition quickly in the current buffer. @@ -126,7 +126,7 @@ ;; "VIRTUAL_ENV=/path/to/env/")) ;; (python-shell-exec-path . ("/path/to/env/bin/")) -;; Since the above is cumbersome and can be programatically +;; Since the above is cumbersome and can be programmatically ;; calculated, the variable `python-shell-virtualenv-path' is ;; provided. When this variable is set with the path of the ;; virtualenv to use, `process-environment' and `exec-path' get proper @@ -426,7 +426,7 @@ This variant of `rx' supports common python named REGEXPS." ;; Extra: "__all__" "__doc__" "__name__" "__package__") symbol-end) . font-lock-builtin-face) - ;; asignations + ;; assignments ;; support for a = b = c = 5 (,(lambda (limit) (let ((re (python-rx (group (+ (any word ?. ?_))) @@ -703,7 +703,7 @@ START is the buffer position where the sexp starts." ('inside-string (goto-char context-start) (current-indentation)) - ;; After backslash we have several posibilities + ;; After backslash we have several possibilities. ('after-backslash (cond ;; Check if current line is a dot continuation. For this @@ -758,12 +758,12 @@ START is the buffer position where the sexp starts." (current-column)))) (t (forward-line -1) - (goto-char (python-info-beginning-of-backlash)) + (goto-char (python-info-beginning-of-backslash)) (if (save-excursion (and (forward-line -1) (goto-char - (or (python-info-beginning-of-backlash) (point))) + (or (python-info-beginning-of-backslash) (point))) (python-info-line-ends-backslash-p))) ;; The two previous lines ended in a backslash so we must ;; respect previous line indentation. @@ -776,7 +776,7 @@ START is the buffer position where the sexp starts." ;; correctly ('inside-paren (cond - ;; If current line closes the outtermost open paren use the + ;; If current line closes the outermost open paren use the ;; current indentation of the context-start line. ((save-excursion (skip-syntax-forward "\s" (line-end-position)) @@ -1261,7 +1261,7 @@ Restart the python shell after changing this variable for it to take effect." (defcustom python-shell-send-setup-max-wait 5 "Seconds to wait for process output before code setup. -If output is received before the especified time then control is +If output is received before the specified time then control is returned in that moment and not after waiting." :type 'integer :group 'python @@ -1811,7 +1811,7 @@ completions on the current context." (overlay-start comint-last-prompt-overlay) (overlay-end comint-last-prompt-overlay)))) (completion-context - ;; Check wether a prompt matches a pdb string, an import statement + ;; Check whether a prompt matches a pdb string, an import statement ;; or just the standard prompt and use the correct ;; python-shell-completion-*-code string (cond ((and (> (length python-shell-completion-pdb-string-code) 0) @@ -2474,7 +2474,7 @@ It can contain a \"%s\" which will be replaced with the root name." (defun python-imenu-make-element-tree (element-list full-element plain-index) "Make a tree from plain alist of module names. -ELEMENT-LIST is the defun name splitted by \".\" and FULL-ELEMENT +ELEMENT-LIST is the defun name split by \".\" and FULL-ELEMENT is the same thing, the difference is that FULL-ELEMENT remains untouched in all recursive calls. Argument PLAIN-INDEX is the calculated plain index used to build the tree." @@ -2497,7 +2497,7 @@ Argument PLAIN-INDEX is the calculated plain index used to build the tree." (push (cons subelement-name subelement-point) python-imenu-index-alist) (when (not (listp (cdr path-ref))) - ;; Modifiy root cdr to be a list + ;; Modify root cdr to be a list. (setcdr path-ref (list (cons (format python-imenu-subtree-root-label (car path-ref)) @@ -2644,8 +2644,8 @@ With optional argument LINE-NUMBER, check that line instead." (when (equal (char-before) ?\\) (point-marker))))) -(defun python-info-beginning-of-backlash (&optional line-number) - "Return the point where the backlashed line start. +(defun python-info-beginning-of-backslash (&optional line-number) + "Return the point where the backslashed line start. Optional argument LINE-NUMBER forces the line number to check against." (save-excursion (save-restriction -- cgit v1.2.1 From a633e6a2b408d34482a9fb689585c6a8a2aea2aa Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Mon, 18 Jun 2012 14:04:21 -0400 Subject: Add coding cookie --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index dfa72a3084a..db21d800e70 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,4 +1,4 @@ -;;; python.el --- Python's flying circus support for Emacs +;;; python.el --- Python's flying circus support for Emacs -*- coding: utf-8 -*- ;; Copyright (C) 2003-2012 Free Software Foundation, Inc. -- cgit v1.2.1 From 35647f79fdd4c5b0050ddb1249659d2a2edb532e Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Mon, 18 Jun 2012 14:23:59 -0400 Subject: Fix python.el obsolescence versions * lisp/progmodes/python.el (python-proc, python-buffer) (python-send-receive, python-send-string): Fix obsolete versions. --- lisp/progmodes/python.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index db21d800e70..3cbd744e3e7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1569,10 +1569,10 @@ there for compatibility with CEDET.") (get-buffer-process proc-buffer-name))) (define-obsolete-function-alias - 'python-proc 'python-shell-internal-get-or-create-process "23.3") + 'python-proc 'python-shell-internal-get-or-create-process "24.2") (define-obsolete-variable-alias - 'python-buffer 'python-shell-internal-buffer "23.3") + 'python-buffer 'python-shell-internal-buffer "24.2") (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. @@ -1627,10 +1627,10 @@ Returns the output. See `python-shell-send-string-no-output'." (python-shell-internal-get-or-create-process) nil)) (define-obsolete-function-alias - 'python-send-receive 'python-shell-internal-send-string "23.3") + 'python-send-receive 'python-shell-internal-send-string "24.2") (define-obsolete-function-alias - 'python-send-string 'python-shell-internal-send-string "23.3") + 'python-send-string 'python-shell-internal-send-string "24.2") (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." -- cgit v1.2.1 From 25f09295fa146e9e4569f16f2eb41f3452383af9 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 18 Jun 2012 16:41:23 -0400 Subject: * lisp/progmodes/python.el (python-rx-constituents): Move backquote. (python-skeleton-define, python-define-auxiliary-skeleton): Use `declare'. --- lisp/progmodes/python.el | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3cbd744e3e7..8844480a95f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -308,29 +308,31 @@ (eval-when-compile (defconst python-rx-constituents - (list - `(block-start . ,(rx symbol-start + `((block-start . ,(rx symbol-start (or "def" "class" "if" "elif" "else" "try" "except" "finally" "for" "while" "with") symbol-end)) - `(decorator . ,(rx line-start (* space) ?@ (any letter ?_) + (decorator . ,(rx line-start (* space) ?@ (any letter ?_) (* (any word ?_)))) - `(defun . ,(rx symbol-start (or "def" "class") symbol-end)) - `(if-name-main . ,(rx line-start "if" (+ space) "__name__" + (defun . ,(rx symbol-start (or "def" "class") symbol-end)) + (if-name-main . ,(rx line-start "if" (+ space) "__name__" (+ space) "==" (+ space) (any ?' ?\") "__main__" (any ?' ?\") (* space) ?:)) - `(symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) - `(open-paren . ,(rx (or "{" "[" "("))) - `(close-paren . ,(rx (or "}" "]" ")"))) - `(simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) - `(not-simple-operator . ,(rx + (symbol-name . ,(rx (any letter ?_) (* (any word ?_)))) + (open-paren . ,(rx (or "{" "[" "("))) + (close-paren . ,(rx (or "}" "]" ")"))) + (simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))) + ;; FIXME: rx should support (not simple-operator). + (not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))) - `(operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" + ;; FIXME: Use regexp-opt. + (operator . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" "=" "%" "**" "//" "<<" ">>" "<=" "!=" "==" ">=" "is" "not"))) - `(assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" + ;; FIXME: Use regexp-opt. + (assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" ">>=" "<<=" "&=" "^=" "|=")))) "Additional Python specific sexps for `python-rx'")) @@ -2146,6 +2148,7 @@ the if condition." "Define a `python-mode' skeleton using NAME DOC and SKEL. The skeleton will be bound to python-skeleton-NAME and will be added to `python-mode-abbrev-table'." + (declare (indent 2)) (let* ((name (symbol-name name)) (function-name (intern (concat "python-skeleton-" name)))) `(progn @@ -2156,11 +2159,11 @@ be added to `python-mode-abbrev-table'." ,(or doc (format "Insert %s statement." name)) ,@skel)))) -(put 'python-skeleton-define 'lisp-indent-function 2) (defmacro python-define-auxiliary-skeleton (name doc &optional &rest skel) "Define a `python-mode' auxiliary skeleton using NAME DOC and SKEL. The skeleton will be bound to python-skeleton-NAME." + (declare (indent 2)) (let* ((name (symbol-name name)) (function-name (intern (concat "python-skeleton--" name))) (msg (format @@ -2176,7 +2179,6 @@ The skeleton will be bound to python-skeleton-NAME." (unless (y-or-n-p ,msg) (signal 'quit t)) ,@skel))) -(put 'python-define-auxiliary-skeleton 'lisp-indent-function 2) (python-define-auxiliary-skeleton else nil) -- cgit v1.2.1 From 68f12411893785de1cfc2c24ec36059e49af5d55 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Tue, 19 Jun 2012 09:04:47 -0700 Subject: * lisp/progmodes/python.el (python-mode): Derive from prog-mode. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8844480a95f..a43dc1eb1d3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2802,7 +2802,7 @@ Optional argument DIRECTION defines the direction to move to." ;;;###autoload -(define-derived-mode python-mode fundamental-mode "Python" +(define-derived-mode python-mode prog-mode "Python" "Major mode for editing Python files. \\{python-mode-map} -- cgit v1.2.1 From 0e9e6c6abce029feda6cced84cb1fa2dc1d6d1c1 Mon Sep 17 00:00:00 2001 From: Chong Yidong Date: Sat, 23 Jun 2012 17:28:10 +0800 Subject: Mark python-mode abbrevs as system abbrevs. * progmodes/python.el (python-skeleton-define): Mark abbrevs as system abbrevs. * ansi-color.el (ansi-color-apply-on-region): Doc fix. --- lisp/progmodes/python.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a43dc1eb1d3..ddedbdb7ddc 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2152,7 +2152,8 @@ be added to `python-mode-abbrev-table'." (let* ((name (symbol-name name)) (function-name (intern (concat "python-skeleton-" name)))) `(progn - (define-abbrev python-mode-abbrev-table ,name "" ',function-name) + (define-abbrev python-mode-abbrev-table ,name "" ',function-name + :system t) (setq python-skeleton-available (cons ',function-name python-skeleton-available)) (define-skeleton ,function-name -- cgit v1.2.1 From 032d23ab664d571be03f5661034d1962baa4f5ae Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 16 Jul 2012 10:13:01 -0300 Subject: * progmodes/python.el: Enhancements to navigation commands. (python-nav-backward-sentence) (python-nav-forward-sentence): Remove. (python-nav-backward-statement, python-nav-forward-statement) (python-nav-statement-start, python-nav-statement-end) (python-nav-backward-block, python-nav-forward-block) (python-nav-block-start, python-nav-block-end) (python-nav-forward-sexp-function) (python-info-current-line-comment-p) (python-info-current-line-empty-p): New functions. (python-indent-context): Use `python-nav-statement-start'. --- lisp/progmodes/python.el | 226 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 201 insertions(+), 25 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ddedbdb7ddc..fe9faf54046 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -46,11 +46,13 @@ ;; Movement: `beginning-of-defun' and `end-of-defun' functions are ;; properly implemented. There are also specialized -;; `forward-sentence' and `backward-sentence' replacements -;; (`python-nav-forward-sentence', `python-nav-backward-sentence' -;; respectively). Extra functions `python-nav-sentence-start' and -;; `python-nav-sentence-end' are included to move to the beginning and -;; to the end of a sentence while taking care of multiline definitions. +;; `forward-sentence' and `backward-sentence' replacements called +;; `python-nav-forward-block', `python-nav-backward-block' +;; respectively which navigate between beginning of blocks of code. +;; Extra functions `python-nav-forward-statement', +;; `python-nav-backward-statement', `python-nav-statement-start', +;; `python-nav-statement-end', `python-nav-block-start' and +;; `python-nav-block-end' are included but no bound to any key. ;; `python-nav-jump-to-defun' is provided and allows jumping to a ;; function or class definition quickly in the current buffer. @@ -227,10 +229,10 @@ (let ((map (make-sparse-keymap))) ;; Movement (substitute-key-definition 'backward-sentence - 'python-nav-backward-sentence + 'python-nav-backward-block map global-map) (substitute-key-definition 'forward-sentence - 'python-nav-forward-sentence + 'python-nav-forward-block map global-map) (define-key map "\C-c\C-j" 'python-nav-jump-to-defun) ;; Indent specific @@ -664,7 +666,7 @@ START is the buffer position where the sexp starts." ((setq start (save-excursion (back-to-indentation) (python-util-forward-comment -1) - (python-nav-sentence-start) + (python-nav-statement-start) (point-marker))) 'after-line) ;; Do not indent @@ -1097,10 +1099,10 @@ Returns nil if point is not in a def or class." (python-info-ppss-context-type)) (forward-line 1))))))) -(defun python-nav-sentence-start () - "Move to start of current sentence." +(defun python-nav-statement-start () + "Move to start of current statement." (interactive "^") - (while (and (not (back-to-indentation)) + (while (and (or (back-to-indentation) t) (not (bobp)) (when (or (save-excursion @@ -1110,8 +1112,8 @@ Returns nil if point is not in a def or class." (python-info-ppss-context 'paren)) (forward-line -1))))) -(defun python-nav-sentence-end () - "Move to end of current sentence." +(defun python-nav-statement-end () + "Move to end of current statement." (interactive "^") (while (and (goto-char (line-end-position)) (not (eobp)) @@ -1121,28 +1123,185 @@ Returns nil if point is not in a def or class." (python-info-ppss-context 'paren)) (forward-line 1))))) -(defun python-nav-backward-sentence (&optional arg) - "Move backward to start of sentence. With ARG, do it arg times. -See `python-nav-forward-sentence' for more information." +(defun python-nav-backward-statement (&optional arg) + "Move backward to previous statement. +With ARG, repeat. See `python-nav-forward-statement'." (interactive "^p") (or arg (setq arg 1)) - (python-nav-forward-sentence (- arg))) + (python-nav-forward-statement (- arg))) -(defun python-nav-forward-sentence (&optional arg) - "Move forward to next end of sentence. With ARG, repeat. -With negative argument, move backward repeatedly to start of sentence." +(defun python-nav-forward-statement (&optional arg) + "Move forward to next statement. +With ARG, repeat. With negative argument, move ARG times +backward to previous statement." (interactive "^p") (or arg (setq arg 1)) (while (> arg 0) + (python-nav-statement-end) (python-util-forward-comment) - (python-nav-sentence-end) - (forward-line 1) + (python-nav-statement-start) (setq arg (1- arg))) (while (< arg 0) - (python-nav-sentence-end) + (python-nav-statement-start) (python-util-forward-comment -1) - (python-nav-sentence-start) - (forward-line -1) + (python-nav-statement-start) + (setq arg (1+ arg)))) + +(defun python-nav-block-start () + "Move to start of current block." + (interactive "^") + (let ((starting-pos (point)) + (block-regexp (python-rx + line-start (* whitespace) block-start))) + (if (progn + (python-nav-statement-start) + (looking-at (python-rx block-start))) + (point-marker) + ;; Go to first line beginning a statement + (while (and (not (bobp)) + (or (and (python-nav-statement-start) nil) + (python-info-current-line-comment-p) + (python-info-current-line-empty-p))) + (forward-line -1)) + (let ((block-matching-indent + (- (current-indentation) python-indent-offset))) + (while + (and (python-nav-backward-block) + (> (current-indentation) block-matching-indent))) + (if (and (looking-at (python-rx block-start)) + (= (current-indentation) block-matching-indent)) + (point-marker) + (and (goto-char starting-pos) nil)))))) + +(defun python-nav-block-end () + "Move to end of current block." + (interactive "^") + (when (python-nav-block-start) + (let ((block-indentation (current-indentation))) + (python-nav-statement-end) + (while (and (forward-line 1) + (not (eobp)) + (or (and (> (current-indentation) block-indentation) + (or (python-nav-statement-end) t)) + (python-info-current-line-comment-p) + (python-info-current-line-empty-p)))) + (python-util-forward-comment -1) + (point-marker)))) + +(defun python-nav-backward-block (&optional arg) + "Move backward to previous block of code. +With ARG, repeat. See `python-nav-forward-block'." + (interactive "^p") + (or arg (setq arg 1)) + (python-nav-forward-block (- arg))) + +(defun python-nav-forward-block (&optional arg) + "Move forward to next block of code. +With ARG, repeat. With negative argument, move ARG times +backward to previous block." + (interactive "^p") + (or arg (setq arg 1)) + (let ((block-start-regexp + (python-rx line-start (* whitespace) block-start)) + (starting-pos (point))) + (while (> arg 0) + (python-nav-statement-end) + (while (and + (re-search-forward block-start-regexp nil t) + (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment) + (python-info-ppss-context 'paren)))) + (setq arg (1- arg))) + (while (< arg 0) + (python-nav-statement-start) + (while (and + (re-search-backward block-start-regexp nil t) + (or (python-info-ppss-context 'string) + (python-info-ppss-context 'comment) + (python-info-ppss-context 'paren)))) + (setq arg (1+ arg))) + (python-nav-statement-start) + (if (not (looking-at (python-rx block-start))) + (and (goto-char starting-pos) nil) + (and (not (= (point) starting-pos)) (point-marker))))) + +(defun python-nav-forward-sexp-function (&optional arg) + "Move forward across one block of code. +With ARG, do it that many times. Negative arg -N means +move backward N times." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (let ((block-starting-pos + (save-excursion (python-nav-block-start))) + (block-ending-pos + (save-excursion (python-nav-block-end))) + (next-block-starting-pos + (save-excursion (python-nav-forward-block)))) + (cond ((not block-starting-pos) + (python-nav-forward-block)) + ((= (point) block-starting-pos) + (if (or (not next-block-starting-pos) + (< block-ending-pos next-block-starting-pos)) + (python-nav-block-end) + (python-nav-forward-block))) + ((= block-ending-pos (point)) + (let ((parent-block-end-pos + (save-excursion + (python-util-forward-comment) + (python-nav-block-start) + (python-nav-block-end)))) + (if (and parent-block-end-pos + (or (not next-block-starting-pos) + (> next-block-starting-pos parent-block-end-pos))) + (goto-char parent-block-end-pos) + (python-nav-forward-block)))) + (t (python-nav-block-end)))) + (setq arg (1- arg))) + (while (< arg 0) + (let* ((block-starting-pos + (save-excursion (python-nav-block-start))) + (block-ending-pos + (save-excursion (python-nav-block-end))) + (prev-block-ending-pos + (save-excursion (when (python-nav-backward-block) + (python-nav-block-end)))) + (prev-block-parent-ending-pos + (save-excursion + (when prev-block-ending-pos + (goto-char prev-block-ending-pos) + (python-util-forward-comment) + (python-nav-block-start) + (python-nav-block-end))))) + (cond ((not block-ending-pos) + (and (python-nav-backward-block) + (python-nav-block-end))) + ((= (point) block-ending-pos) + (let ((candidates)) + (dolist (name + '(prev-block-parent-ending-pos + prev-block-ending-pos + block-ending-pos + block-starting-pos)) + (when (and (symbol-value name) + (< (symbol-value name) (point))) + (add-to-list 'candidates (symbol-value name)))) + (goto-char (apply 'max candidates)))) + ((> (point) block-ending-pos) + (python-nav-block-end)) + ((= (point) block-starting-pos) + (if (not (> (point) (or prev-block-ending-pos (point)))) + (python-nav-backward-block) + (goto-char prev-block-ending-pos) + (let ((parent-block-ending-pos + (save-excursion + (python-nav-forward-sexp-function) + (and (not (looking-at (python-rx block-start))) + (point))))) + (when (and parent-block-ending-pos + (> parent-block-ending-pos prev-block-ending-pos)) + (goto-char parent-block-ending-pos))))) + (t (python-nav-block-start)))) (setq arg (1+ arg)))) (defvar python-nav-list-defun-positions-cache nil) @@ -2766,6 +2925,20 @@ The type returned can be 'comment, 'string or 'paren." (beginning-of-line 1) (looking-at python-nav-beginning-of-defun-regexp)))) +(defun python-info-current-line-comment-p () + "Check if current line is a comment line." + (char-equal (or (char-after (+ (point) (current-indentation))) ?_) ?#)) + +(defun python-info-current-line-empty-p () + "Check if current line is empty, ignoring whitespace." + (save-excursion + (beginning-of-line 1) + (looking-at + (python-rx line-start (* whitespace) + (group (* not-newline)) + (* whitespace) line-end)) + (string-equal "" (match-string-no-properties 1)))) + ;;; Utility functions @@ -2818,6 +2991,9 @@ if that value is non-nil." (set (make-local-variable 'parse-sexp-lookup-properties) t) (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'forward-sexp-function) + 'python-nav-forward-sexp-function) + (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords nil nil nil nil)) -- cgit v1.2.1 From 758e556a7ab8f61c007e34310ba399a9aaf15362 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 16 Jul 2012 14:18:39 -0300 Subject: * progmodes/python.el: Simplified imenu implementation. (python-nav-jump-to-defun): Remove command. (python-mode-map): Use `imenu' instead. (python-nav-list-defun-positions-cache) (python-imenu-include-defun-type, python-imenu-make-tree) (python-imenu-subtree-root-label, python-imenu-index-alist): Remove vars. (python-nav-list-defun-positions, python-nav-read-defun) (python-imenu-tree-assoc, python-imenu-make-element-tree) (python-imenu-make-tree, python-imenu-create-index): Remove functions. (python-mode): Update to interact with imenu by setting `imenu-extract-index-name-function' only. --- lisp/progmodes/python.el | 186 ++--------------------------------------------- 1 file changed, 8 insertions(+), 178 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index fe9faf54046..e0a58d1523e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -53,8 +53,6 @@ ;; `python-nav-backward-statement', `python-nav-statement-start', ;; `python-nav-statement-end', `python-nav-block-start' and ;; `python-nav-block-end' are included but no bound to any key. -;; `python-nav-jump-to-defun' is provided and allows jumping to a -;; function or class definition quickly in the current buffer. ;; Shell interaction: is provided and allows you to execute easily any ;; block of code of your current buffer in an inferior Python process. @@ -168,10 +166,10 @@ ;; might guessed you should run `python-shell-send-buffer' from time ;; to time to get better results too. -;; imenu: This mode supports imenu. It builds a plain or tree menu -;; depending on the value of `python-imenu-make-tree'. Also you can -;; customize if menu items should include its type using -;; `python-imenu-include-defun-type'. +;; imenu: This mode supports imenu in its most basic form, letting it +;; build the necessary alist via `imenu-default-create-index-function' +;; by having set `imenu-extract-index-name-function' to +;; `python-info-current-defun'. ;; If you used python-mode.el you probably will miss auto-indentation ;; when inserting newlines. To achieve the same behavior you have @@ -234,7 +232,7 @@ (substitute-key-definition 'forward-sentence 'python-nav-forward-block map global-map) - (define-key map "\C-c\C-j" 'python-nav-jump-to-defun) + (define-key map "\C-c\C-j" 'imenu) ;; Indent specific (define-key map "\177" 'python-indent-dedent-line-backspace) (define-key map (kbd "") 'python-indent-dedent-line) @@ -275,7 +273,7 @@ :help "Go to end of definition around point"] ["Mark def/class" mark-defun :help "Mark outermost definition around point"] - ["Jump to def/class" python-nav-jump-to-defun + ["Jump to def/class" imenu :help "Jump to a class or function definition"] "--" ("Skeletons") @@ -1304,62 +1302,6 @@ move backward N times." (t (python-nav-block-start)))) (setq arg (1+ arg)))) -(defvar python-nav-list-defun-positions-cache nil) -(make-variable-buffer-local 'python-nav-list-defun-positions-cache) - -(defun python-nav-list-defun-positions (&optional include-type rescan) - "Make an Alist of defun names and point markers for current buffer. -When optional argument INCLUDE-TYPE is non-nil the type is -included the defun name. With optional argument RESCAN the -`python-nav-list-defun-positions-cache' is invalidated and the -list of defun is regenerated again." - (if (and python-nav-list-defun-positions-cache (not rescan)) - python-nav-list-defun-positions-cache - (let ((defs)) - (save-restriction - (widen) - (save-excursion - (goto-char (point-max)) - (while (re-search-backward python-nav-beginning-of-defun-regexp nil t) - (when (and (not (python-info-ppss-context 'string)) - (not (python-info-ppss-context 'comment)) - (not (python-info-ppss-context 'parent))) - (add-to-list - 'defs (cons - (python-info-current-defun include-type) - (point-marker))))) - (setq python-nav-list-defun-positions-cache defs)))))) - -(defun python-nav-read-defun (&optional rescan) - "Read a defun name of current buffer and return its point marker. -A cons cell with the form (DEFUN-NAME . POINT-MARKER) is returned -when defun is completed, else nil. With optional argument RESCAN -forces `python-nav-list-defun-positions' to invalidate its -cache." - (let ((defs (python-nav-list-defun-positions nil rescan))) - (minibuffer-with-setup-hook - (lambda () - (setq minibuffer-completion-table (mapcar 'car defs))) - (let ((stringdef - (read-from-minibuffer - "Jump to definition: " nil - minibuffer-local-must-match-map))) - (when (not (string= stringdef "")) - (assoc-string stringdef defs)))))) - -(defun python-nav-jump-to-defun (def) - "Jump to the definition of DEF in current file. -Locations are cached; use a `C-u' prefix argument to force a -rescan." - (interactive - (list (python-nav-read-defun current-prefix-arg))) - (when (not (called-interactively-p 'interactive)) - (setq def (assoc-string def (python-nav-list-defun-positions)))) - (let ((def-marker (cdr def))) - (when (markerp def-marker) - (goto-char (marker-position def-marker)) - (back-to-indentation)))) - ;;; Shell integration @@ -2602,119 +2544,6 @@ Interactively, prompt for symbol." (message (python-eldoc--get-doc-at-point symbol process))))) -;;; Imenu - -(defcustom python-imenu-include-defun-type t - "Non-nil make imenu items to include its type." - :type 'boolean - :group 'python - :safe 'booleanp) - -(defcustom python-imenu-make-tree t - "Non-nil make imenu to build a tree menu. -Set to nil for speed." - :type 'boolean - :group 'python - :safe 'booleanp) - -(defcustom python-imenu-subtree-root-label "" - "Label displayed to navigate to root from a subtree. -It can contain a \"%s\" which will be replaced with the root name." - :type 'string - :group 'python - :safe 'stringp) - -(defvar python-imenu-index-alist nil - "Calculated index tree for imenu.") - -(defun python-imenu-tree-assoc (keylist tree) - "Using KEYLIST traverse TREE." - (if keylist - (python-imenu-tree-assoc (cdr keylist) - (ignore-errors (assoc (car keylist) tree))) - tree)) - -(defun python-imenu-make-element-tree (element-list full-element plain-index) - "Make a tree from plain alist of module names. -ELEMENT-LIST is the defun name split by \".\" and FULL-ELEMENT -is the same thing, the difference is that FULL-ELEMENT remains -untouched in all recursive calls. -Argument PLAIN-INDEX is the calculated plain index used to build the tree." - (when (not (python-imenu-tree-assoc full-element python-imenu-index-alist)) - (when element-list - (let* ((subelement-point (cdr (assoc - (mapconcat #'identity full-element ".") - plain-index))) - (subelement-name (car element-list)) - (subelement-position (python-util-position - subelement-name full-element)) - (subelement-path (when subelement-position - (butlast - full-element - (- (length full-element) - subelement-position))))) - (let ((path-ref (python-imenu-tree-assoc subelement-path - python-imenu-index-alist))) - (if (not path-ref) - (push (cons subelement-name subelement-point) - python-imenu-index-alist) - (when (not (listp (cdr path-ref))) - ;; Modify root cdr to be a list. - (setcdr path-ref - (list (cons (format python-imenu-subtree-root-label - (car path-ref)) - (cdr (assoc - (mapconcat #'identity - subelement-path ".") - plain-index)))))) - (when (not (assoc subelement-name path-ref)) - (push (cons subelement-name subelement-point) (cdr path-ref)))))) - (python-imenu-make-element-tree (cdr element-list) - full-element plain-index)))) - -(defun python-imenu-make-tree (index) - "Build the imenu alist tree from plain INDEX. - -The idea of this function is that given the alist: - - '((\"Test\" . 100) - (\"Test.__init__\" . 200) - (\"Test.some_method\" . 300) - (\"Test.some_method.another\" . 400) - (\"Test.something_else\" . 500) - (\"test\" . 600) - (\"test.reprint\" . 700) - (\"test.reprint\" . 800)) - -This tree gets built: - - '((\"Test\" . ((\"jump to...\" . 100) - (\"__init__\" . 200) - (\"some_method\" . ((\"jump to...\" . 300) - (\"another\" . 400))) - (\"something_else\" . 500))) - (\"test\" . ((\"jump to...\" . 600) - (\"reprint\" . 700) - (\"reprint\" . 800)))) - -Internally it uses `python-imenu-make-element-tree' to create all -branches for each element." - (setq python-imenu-index-alist nil) - (mapc (lambda (element) - (python-imenu-make-element-tree element element index)) - (mapcar (lambda (element) - (split-string (car element) "\\." t)) index)) - python-imenu-index-alist) - -(defun python-imenu-create-index () - "`imenu-create-index-function' for Python." - (let ((index - (python-nav-list-defun-positions python-imenu-include-defun-type))) - (if python-imenu-make-tree - (python-imenu-make-tree index) - index))) - - ;;; Misc helpers (defun python-info-current-defun (&optional include-type) @@ -3019,7 +2848,8 @@ if that value is non-nil." (add-hook 'post-self-insert-hook 'python-indent-post-self-insert-function nil 'local) - (setq imenu-create-index-function #'python-imenu-create-index) + (set (make-local-variable 'imenu-extract-index-name-function) + #'python-info-current-defun) (set (make-local-variable 'add-log-current-defun-function) #'python-info-current-defun) -- cgit v1.2.1 From 191da00ee25cec5d13f257720ef401fc31203384 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 17 Jul 2012 13:47:58 -0300 Subject: * progmodes/python.el (python-shell-send-string-no-output): Allow accept-process-output to quit, keeping shell process ready for future interactions. Fixes: debbugs:11868 --- lisp/progmodes/python.el | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e0a58d1523e..f6a1fbf3ab7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1701,24 +1701,29 @@ When MSG is non-nil messages the first line of STRING." "Send STRING to PROCESS and inhibit output. When MSG is non-nil messages the first line of STRING. Return the output." - (let* ((output-buffer) + (let* ((output-buffer "") (process (or process (python-shell-get-or-create-process))) (comint-preoutput-filter-functions (append comint-preoutput-filter-functions '(ansi-color-filter-apply (lambda (string) (setq output-buffer (concat output-buffer string)) - ""))))) - (python-shell-send-string string process msg) - (accept-process-output process) - (replace-regexp-in-string - (if (> (length python-shell-prompt-output-regexp) 0) - (format "\n*%s$\\|^%s\\|\n$" - python-shell-prompt-regexp - (or python-shell-prompt-output-regexp "")) - (format "\n*$\\|^%s\\|\n$" - python-shell-prompt-regexp)) - "" output-buffer))) + "")))) + (inhibit-quit t)) + (or + (with-local-quit + (python-shell-send-string string process msg) + (accept-process-output process) + (replace-regexp-in-string + (if (> (length python-shell-prompt-output-regexp) 0) + (format "\n*%s$\\|^%s\\|\n$" + python-shell-prompt-regexp + (or python-shell-prompt-output-regexp "")) + (format "\n*$\\|^%s\\|\n$" + python-shell-prompt-regexp)) + "" output-buffer)) + (with-current-buffer (process-buffer process) + (comint-interrupt-subjob))))) (defun python-shell-internal-send-string (string) "Send STRING to the Internal Python interpreter. -- cgit v1.2.1 From bcdc27d7d4a5914ddbbec86441064bbe8f231a52 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 17 Jul 2012 15:02:53 -0300 Subject: Final renames and doc fixes for movement commands. * progmodes/python.el (python-nav-beginning-of-statement): Rename from python-nav-statement-start. (python-nav-end-of-statement): Rename from python-nav-statement-end. (python-nav-beginning-of-block): Rename from python-nav-block-start. (python-nav-end-of-block): Rename from python-nav-block-end. Fixes: debbugs:11899 --- lisp/progmodes/python.el | 71 +++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 34 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f6a1fbf3ab7..e9b5770dc87 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -50,9 +50,12 @@ ;; `python-nav-forward-block', `python-nav-backward-block' ;; respectively which navigate between beginning of blocks of code. ;; Extra functions `python-nav-forward-statement', -;; `python-nav-backward-statement', `python-nav-statement-start', -;; `python-nav-statement-end', `python-nav-block-start' and -;; `python-nav-block-end' are included but no bound to any key. +;; `python-nav-backward-statement', +;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', +;; `python-nav-beginning-of-block' and `python-nav-end-of-block' are +;; included but no bound to any key. At last but not least the +;; specialized `python-nav-forward-sexp-function' allows easy +;; navigation between code blocks. ;; Shell interaction: is provided and allows you to execute easily any ;; block of code of your current buffer in an inferior Python process. @@ -664,7 +667,7 @@ START is the buffer position where the sexp starts." ((setq start (save-excursion (back-to-indentation) (python-util-forward-comment -1) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (point-marker))) 'after-line) ;; Do not indent @@ -1097,7 +1100,7 @@ Returns nil if point is not in a def or class." (python-info-ppss-context-type)) (forward-line 1))))))) -(defun python-nav-statement-start () +(defun python-nav-beginning-of-statement () "Move to start of current statement." (interactive "^") (while (and (or (back-to-indentation) t) @@ -1110,7 +1113,7 @@ Returns nil if point is not in a def or class." (python-info-ppss-context 'paren)) (forward-line -1))))) -(defun python-nav-statement-end () +(defun python-nav-end-of-statement () "Move to end of current statement." (interactive "^") (while (and (goto-char (line-end-position)) @@ -1135,29 +1138,29 @@ backward to previous statement." (interactive "^p") (or arg (setq arg 1)) (while (> arg 0) - (python-nav-statement-end) + (python-nav-end-of-statement) (python-util-forward-comment) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (setq arg (1- arg))) (while (< arg 0) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (python-util-forward-comment -1) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (setq arg (1+ arg)))) -(defun python-nav-block-start () +(defun python-nav-beginning-of-block () "Move to start of current block." (interactive "^") (let ((starting-pos (point)) (block-regexp (python-rx line-start (* whitespace) block-start))) (if (progn - (python-nav-statement-start) + (python-nav-beginning-of-statement) (looking-at (python-rx block-start))) (point-marker) ;; Go to first line beginning a statement (while (and (not (bobp)) - (or (and (python-nav-statement-start) nil) + (or (and (python-nav-beginning-of-statement) nil) (python-info-current-line-comment-p) (python-info-current-line-empty-p))) (forward-line -1)) @@ -1171,16 +1174,16 @@ backward to previous statement." (point-marker) (and (goto-char starting-pos) nil)))))) -(defun python-nav-block-end () +(defun python-nav-end-of-block () "Move to end of current block." (interactive "^") - (when (python-nav-block-start) + (when (python-nav-beginning-of-block) (let ((block-indentation (current-indentation))) - (python-nav-statement-end) + (python-nav-end-of-statement) (while (and (forward-line 1) (not (eobp)) (or (and (> (current-indentation) block-indentation) - (or (python-nav-statement-end) t)) + (or (python-nav-end-of-statement) t)) (python-info-current-line-comment-p) (python-info-current-line-empty-p)))) (python-util-forward-comment -1) @@ -1203,7 +1206,7 @@ backward to previous block." (python-rx line-start (* whitespace) block-start)) (starting-pos (point))) (while (> arg 0) - (python-nav-statement-end) + (python-nav-end-of-statement) (while (and (re-search-forward block-start-regexp nil t) (or (python-info-ppss-context 'string) @@ -1211,14 +1214,14 @@ backward to previous block." (python-info-ppss-context 'paren)))) (setq arg (1- arg))) (while (< arg 0) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (while (and (re-search-backward block-start-regexp nil t) (or (python-info-ppss-context 'string) (python-info-ppss-context 'comment) (python-info-ppss-context 'paren)))) (setq arg (1+ arg))) - (python-nav-statement-start) + (python-nav-beginning-of-statement) (if (not (looking-at (python-rx block-start))) (and (goto-char starting-pos) nil) (and (not (= (point) starting-pos)) (point-marker))))) @@ -1231,9 +1234,9 @@ move backward N times." (or arg (setq arg 1)) (while (> arg 0) (let ((block-starting-pos - (save-excursion (python-nav-block-start))) + (save-excursion (python-nav-beginning-of-block))) (block-ending-pos - (save-excursion (python-nav-block-end))) + (save-excursion (python-nav-end-of-block))) (next-block-starting-pos (save-excursion (python-nav-forward-block)))) (cond ((not block-starting-pos) @@ -1241,39 +1244,39 @@ move backward N times." ((= (point) block-starting-pos) (if (or (not next-block-starting-pos) (< block-ending-pos next-block-starting-pos)) - (python-nav-block-end) + (python-nav-end-of-block) (python-nav-forward-block))) ((= block-ending-pos (point)) (let ((parent-block-end-pos (save-excursion (python-util-forward-comment) - (python-nav-block-start) - (python-nav-block-end)))) + (python-nav-beginning-of-block) + (python-nav-end-of-block)))) (if (and parent-block-end-pos (or (not next-block-starting-pos) (> next-block-starting-pos parent-block-end-pos))) (goto-char parent-block-end-pos) (python-nav-forward-block)))) - (t (python-nav-block-end)))) + (t (python-nav-end-of-block)))) (setq arg (1- arg))) (while (< arg 0) (let* ((block-starting-pos - (save-excursion (python-nav-block-start))) + (save-excursion (python-nav-beginning-of-block))) (block-ending-pos - (save-excursion (python-nav-block-end))) + (save-excursion (python-nav-end-of-block))) (prev-block-ending-pos (save-excursion (when (python-nav-backward-block) - (python-nav-block-end)))) + (python-nav-end-of-block)))) (prev-block-parent-ending-pos (save-excursion (when prev-block-ending-pos (goto-char prev-block-ending-pos) (python-util-forward-comment) - (python-nav-block-start) - (python-nav-block-end))))) + (python-nav-beginning-of-block) + (python-nav-end-of-block))))) (cond ((not block-ending-pos) (and (python-nav-backward-block) - (python-nav-block-end))) + (python-nav-end-of-block))) ((= (point) block-ending-pos) (let ((candidates)) (dolist (name @@ -1286,7 +1289,7 @@ move backward N times." (add-to-list 'candidates (symbol-value name)))) (goto-char (apply 'max candidates)))) ((> (point) block-ending-pos) - (python-nav-block-end)) + (python-nav-end-of-block)) ((= (point) block-starting-pos) (if (not (> (point) (or prev-block-ending-pos (point)))) (python-nav-backward-block) @@ -1299,7 +1302,7 @@ move backward N times." (when (and parent-block-ending-pos (> parent-block-ending-pos prev-block-ending-pos)) (goto-char parent-block-ending-pos))))) - (t (python-nav-block-start)))) + (t (python-nav-beginning-of-block)))) (setq arg (1+ arg)))) -- cgit v1.2.1 From 0a60bc107123321438fc1320ab34fcf588ec7128 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 17 Jul 2012 17:27:49 -0300 Subject: New utility functions + python-info-ppss-context fix. * progmodes/python.el (python-info-beginning-of-block-statement-p) (python-info-ppss-comment-or-string-p): New functions. (python-info-ppss-context): Small fix for string check. Fixes: debbugs:11910 --- lisp/progmodes/python.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e9b5770dc87..54486ba5a52 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2584,6 +2584,12 @@ not inside a defun." (when names (mapconcat (lambda (string) string) names ".")))) +(defsubst python-info-beginning-of-block-statement-p () + "Return non-nil if current statement opens a block." + (save-excursion + (python-nav-beginning-of-statement) + (looking-at (python-rx block-start)))) + (defun python-info-closing-block () "Return the point of the block the current line closes." (let ((closing-word (save-excursion @@ -2736,7 +2742,8 @@ character address of the specified TYPE." (and (nth 4 ppss) (nth 8 ppss))) ('string - (nth 8 ppss)) + (and (not (nth 4 ppss)) + (nth 8 ppss))) ('paren (nth 1 ppss)) (t nil)))) @@ -2755,6 +2762,10 @@ The type returned can be 'comment, 'string or 'paren." 'paren) (t nil)))) +(defsubst python-info-ppss-comment-or-string-p () + "Return non-nil if point is inside 'comment or 'string." + (car (member (python-info-ppss-context-type) '(string comment)))) + (defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." (and (not (python-info-ppss-context-type (or syntax-ppss (syntax-ppss)))) -- cgit v1.2.1 From d583cbe6814dee8e976e00c2d71fa874a0310e36 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Wed, 18 Jul 2012 15:40:03 -0300 Subject: * progmodes/python.el: Enhancements to eldoc support. (python-info-current-symbol): New function. (python-eldoc-at-point): Use python-info-current-symbol. (python-info-current-defun): Fix cornercase on first defun scan. (python-eldoc--get-doc-at-point): Use python-info-current-symbol and signal error when no inferior python process is available. --- lisp/progmodes/python.el | 103 +++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 49 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 54486ba5a52..b5474732316 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2487,46 +2487,19 @@ Runs COMMAND, a shell command, as if by `compile'. See (defun python-eldoc--get-doc-at-point (&optional force-input force-process) "Internal implementation to get documentation at point. -If not FORCE-INPUT is passed then what `current-word' returns -will be used. If not FORCE-PROCESS is passed what -`python-shell-get-process' returns is used." +If not FORCE-INPUT is passed then what +`python-info-current-symbol' returns will be used. If not +FORCE-PROCESS is passed what `python-shell-get-process' returns +is used." (let ((process (or force-process (python-shell-get-process)))) (if (not process) - "Eldoc needs an inferior Python process running." - (let* ((current-defun (python-info-current-defun)) - (input (or force-input - (with-syntax-table python-dotty-syntax-table - (if (not current-defun) - (current-word) - (concat current-defun "." (current-word)))))) - (ppss (syntax-ppss)) - (help (when (and - input - (not (string= input (concat current-defun "."))) - (not (or (python-info-ppss-context 'string ppss) - (python-info-ppss-context 'comment ppss)))) - (when (string-match - (concat - (regexp-quote (concat current-defun ".")) - "self\\.") input) - (with-temp-buffer - (insert input) - (goto-char (point-min)) - (forward-word) - (forward-char) - (delete-region - (point-marker) (search-forward "self.")) - (setq input (buffer-substring - (point-min) (point-max))))) - (python-shell-send-string-no-output - (format python-eldoc-string-code input) process)))) - (with-current-buffer (process-buffer process) - (when comint-last-prompt-overlay - (delete-region comint-last-input-end - (overlay-start comint-last-prompt-overlay)))) - (when (and help - (not (string= help "\n"))) - help))))) + (error "Eldoc needs an inferior Python process running") + (let ((input (or force-input + (python-info-current-symbol t)))) + (and input + (python-shell-send-string-no-output + (format python-eldoc-string-code input) + process)))))) (defun python-eldoc-function () "`eldoc-documentation-function' for Python. @@ -2539,17 +2512,16 @@ inferior python process is updated properly." "Get help on SYMBOL using `help'. Interactively, prompt for symbol." (interactive - (let ((symbol (with-syntax-table python-dotty-syntax-table - (current-word))) + (let ((symbol (python-info-current-symbol t)) (enable-recursive-minibuffers t)) (list (read-string (if symbol (format "Describe symbol (default %s): " symbol) "Describe symbol: ") nil nil symbol)))) - (let ((process (python-shell-get-process))) - (if (not process) - (message "Eldoc needs an inferior Python process running.") - (message (python-eldoc--get-doc-at-point symbol process))))) + (message (python-eldoc--get-doc-at-point symbol))) + +(add-to-list 'debug-ignored-errors + "^Eldoc needs an inferior Python process running.") ;;; Misc helpers @@ -2561,18 +2533,27 @@ This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." (let ((names '()) - (min-indent) + (starting-indentation) + (starting-point) (first-run t)) (save-restriction (widen) (save-excursion + (setq starting-point (point-marker)) + (setq starting-indentation (save-excursion + (python-nav-beginning-of-statement) + (current-indentation))) (end-of-line 1) - (setq min-indent (current-indentation)) (while (python-beginning-of-defun-function 1) - (when (or (< (current-indentation) min-indent) - first-run) + (when (or (< (current-indentation) starting-indentation) + (and first-run + (< + starting-point + (save-excursion + (python-end-of-defun-function) + (point-marker))))) (setq first-run nil) - (setq min-indent (current-indentation)) + (setq starting-indentation (current-indentation)) (looking-at python-nav-beginning-of-defun-regexp) (setq names (cons (if (not include-type) @@ -2584,6 +2565,30 @@ not inside a defun." (when names (mapconcat (lambda (string) string) names ".")))) +(defun python-info-current-symbol (&optional replace-self) + "Return current symbol using dotty syntax. +With optional argument REPLACE-SELF convert \"self\" to current +parent defun name." + (let ((name + (and (not (python-info-ppss-comment-or-string-p)) + (with-syntax-table python-dotty-syntax-table + (let ((sym (symbol-at-point))) + (and sym + (substring-no-properties (symbol-name sym)))))))) + (when name + (if (not replace-self) + name + (let ((current-defun (python-info-current-defun))) + (if (not current-defun) + name + (replace-regexp-in-string + (python-rx line-start word-start "self" word-end ?.) + (concat + (mapconcat 'identity + (butlast (split-string current-defun "\\.")) + ".") ".") + name))))))) + (defsubst python-info-beginning-of-block-statement-p () "Return non-nil if current statement opens a block." (save-excursion -- cgit v1.2.1 From d617c457bd9712ea07b138ab6f069dd8ab794b57 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Wed, 18 Jul 2012 16:04:06 -0300 Subject: progmodes/python.el: Set file local vars at end of file and clean tabs. --- lisp/progmodes/python.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b5474732316..23e040366dc 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1,4 +1,4 @@ -;;; python.el --- Python's flying circus support for Emacs -*- coding: utf-8 -*- +;;; python.el --- Python's flying circus support for Emacs ;; Copyright (C) 2003-2012 Free Software Foundation, Inc. @@ -2262,7 +2262,7 @@ be added to `python-mode-abbrev-table'." (function-name (intern (concat "python-skeleton-" name)))) `(progn (define-abbrev python-mode-abbrev-table ,name "" ',function-name - :system t) + :system t) (setq python-skeleton-available (cons ',function-name python-skeleton-available)) (define-skeleton ,function-name @@ -2911,4 +2911,10 @@ if that value is non-nil." (provide 'python) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: + ;;; python.el ends here -- cgit v1.2.1 From 1d29cc7da73dde538c97d029723b8a5a1be6cea1 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Wed, 18 Jul 2012 21:55:55 -0300 Subject: Enhancements to ppss related code (thanks Stefan). * progmodes/python.el (python-indent-context) (python-indent-calculate-indentation, python-indent-dedent-line) (python-indent-electric-colon, python-nav-forward-block) (python-mode-abbrev-table) (python-info-assignment-continuation-line-p): Simplified checks for ppss context. (python-info-continuation-line-p): Cleanup. (python-info-ppss-context): Do not catch 'quote. (python-info-ppss-context-type) (python-info-ppss-comment-or-string-p): Simplify. --- lisp/progmodes/python.el | 73 ++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 23e040366dc..132951aedc8 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -656,9 +656,7 @@ START is the buffer position where the sexp starts." (while (and (re-search-backward (python-rx block-start) nil t) (or - (python-info-ppss-context 'string) - (python-info-ppss-context 'comment) - (python-info-ppss-context 'paren) + (python-info-ppss-context-type) (python-info-continuation-line-p)))) (when (looking-at (python-rx block-start)) (point-marker))))) @@ -726,13 +724,9 @@ START is the buffer position where the sexp starts." (goto-char (line-end-position)) (while (and (re-search-backward "\\." (line-beginning-position) t) - (or (python-info-ppss-context 'comment) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)))) + (python-info-ppss-context-type))) (if (and (looking-at "\\.") - (not (or (python-info-ppss-context 'comment) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)))) + (not (python-info-ppss-context-type))) ;; The indentation is the same column of the ;; first matching dot that's not inside a ;; comment, a string or a paren @@ -888,8 +882,7 @@ See `python-indent-line' for details." (defun python-indent-dedent-line () "De-indent current line." (interactive "*") - (when (and (not (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment))) + (when (and (not (python-info-ppss-comment-or-string-p)) (<= (point-marker) (save-excursion (back-to-indentation) (point-marker))) @@ -980,8 +973,7 @@ With numeric ARG, just insert that many colons. With (when (and (not arg) (eolp) (not (equal ?: (char-after (- (point-marker) 2)))) - (not (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment)))) + (not (python-info-ppss-comment-or-string-p))) (let ((indentation (current-indentation)) (calculated-indentation (python-indent-calculate-indentation))) (python-info-closing-block-message) @@ -1209,17 +1201,13 @@ backward to previous block." (python-nav-end-of-statement) (while (and (re-search-forward block-start-regexp nil t) - (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment) - (python-info-ppss-context 'paren)))) + (python-info-ppss-context-type))) (setq arg (1- arg))) (while (< arg 0) (python-nav-beginning-of-statement) (while (and (re-search-backward block-start-regexp nil t) - (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment) - (python-info-ppss-context 'paren)))) + (python-info-ppss-context-type))) (setq arg (1+ arg))) (python-nav-beginning-of-statement) (if (not (looking-at (python-rx block-start))) @@ -2249,8 +2237,7 @@ the if condition." ;; Only expand in code. :enable-function (lambda () (and - (not (or (python-info-ppss-context 'string) - (python-info-ppss-context 'comment))) + (not (python-info-ppss-comment-or-string-p)) python-skeleton-autoinsert))) (defmacro python-skeleton-define (name doc &rest skel) @@ -2686,24 +2673,20 @@ where the continued line ends." (cond ((equal context-type 'paren) ;; Lines inside a paren are always a continuation line ;; (except the first one). - (when (equal (python-info-ppss-context-type) 'paren) - (python-util-forward-comment -1) - (python-util-forward-comment -1) - (point-marker))) - ((or (equal context-type 'comment) - (equal context-type 'string)) + (python-util-forward-comment -1) + (point-marker)) + ((member context-type '(string comment)) ;; move forward an roll again (goto-char context-start) (python-util-forward-comment) (python-info-continuation-line-p)) (t - ;; Not within a paren, string or comment, the only way we are - ;; dealing with a continuation line is that previous line - ;; contains a backslash, and this can only be the previous line - ;; from current + ;; Not within a paren, string or comment, the only way + ;; we are dealing with a continuation line is that + ;; previous line contains a backslash, and this can + ;; only be the previous line from current (back-to-indentation) (python-util-forward-comment -1) - (python-util-forward-comment -1) (when (and (equal (1- line-start) (line-number-at-pos)) (python-info-line-ends-backslash-p)) (point-marker)))))))) @@ -2731,45 +2714,37 @@ operator." assignment-operator not-simple-operator) (line-end-position) t) - (not (or (python-info-ppss-context 'string) - (python-info-ppss-context 'paren) - (python-info-ppss-context 'comment))))) + (not (python-info-ppss-context-type)))) (skip-syntax-forward "\s") (point-marker))))) (defun python-info-ppss-context (type &optional syntax-ppss) "Return non-nil if point is on TYPE using SYNTAX-PPSS. -TYPE can be 'comment, 'string or 'paren. It returns the start +TYPE can be `comment', `string' or `paren'. It returns the start character address of the specified TYPE." (let ((ppss (or syntax-ppss (syntax-ppss)))) (case type - ('comment + (comment (and (nth 4 ppss) (nth 8 ppss))) - ('string + (string (and (not (nth 4 ppss)) (nth 8 ppss))) - ('paren + (paren (nth 1 ppss)) (t nil)))) (defun python-info-ppss-context-type (&optional syntax-ppss) "Return the context type using SYNTAX-PPSS. -The type returned can be 'comment, 'string or 'paren." +The type returned can be `comment', `string' or `paren'." (let ((ppss (or syntax-ppss (syntax-ppss)))) (cond - ((and (nth 4 ppss) - (nth 8 ppss)) - 'comment) - ((nth 8 ppss) - 'string) - ((nth 1 ppss) - 'paren) - (t nil)))) + ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string)) + ((nth 1 ppss) 'paren)))) (defsubst python-info-ppss-comment-or-string-p () "Return non-nil if point is inside 'comment or 'string." - (car (member (python-info-ppss-context-type) '(string comment)))) + (nth 8 (syntax-ppss))) (defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." -- cgit v1.2.1 From a90dfb95112ebff530ec5c7167c1402b10c2e49b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Fri, 27 Jul 2012 09:38:19 -0300 Subject: * lisp/progmodes/python.el (python-mode-map): Added keybinding for run-python. (python-shell-make-comint): Fix pop-to-buffer call. (run-python): Autoload. New arg SHOW. (python-shell-get-or-create-process): Do not pop python process buffer. --- lisp/progmodes/python.el | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 132951aedc8..ab364a5318a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -250,6 +250,7 @@ (define-key map "\C-c\C-tt" 'python-skeleton-try) (define-key map "\C-c\C-tw" 'python-skeleton-while) ;; Shell interaction + (define-key map "\C-c\C-p" 'run-python) (define-key map "\C-c\C-s" 'python-shell-send-string) (define-key map "\C-c\C-r" 'python-shell-send-region) (define-key map "\C-\M-x" 'python-shell-send-defun) @@ -1571,30 +1572,33 @@ non-nil the buffer is shown." (with-current-buffer buffer (inferior-python-mode) (python-util-clone-local-variables current-buffer)))) - (when pop - (pop-to-buffer proc-buffer-name)) + (and pop (pop-to-buffer proc-buffer-name t)) proc-buffer-name))) -(defun run-python (dedicated cmd) +;;;###autoload +(defun run-python (cmd &optional dedicated show) "Run an inferior Python process. Input and output via buffer named after `python-shell-buffer-name'. If there is a process already running in that buffer, just switch to it. -With argument, allows you to define DEDICATED, so a dedicated -process for the current buffer is open, and define CMD so you can -edit the command used to call the interpreter (default is value -of `python-shell-interpreter' and arguments defined in -`python-shell-interpreter-args'). Runs the hook -`inferior-python-mode-hook' (after the `comint-mode-hook' is -run). -\(Type \\[describe-mode] in the process buffer for a list of commands.)" + +With argument, allows you to define CMD so you can edit the +command used to call the interpreter and define DEDICATED, so a +dedicated process for the current buffer is open. When numeric +prefix arg is other than 0 or 4 do not SHOW. + +Runs the hook `inferior-python-mode-hook' (after the +`comint-mode-hook' is run). \(Type \\[describe-mode] in the +process buffer for a list of commands.)" (interactive (if current-prefix-arg (list + (read-string "Run Python: " (python-shell-parse-command)) (y-or-n-p "Make dedicated process? ") - (read-string "Run Python: " (python-shell-parse-command))) - (list nil (python-shell-parse-command)))) - (python-shell-make-comint cmd (python-shell-get-process-name dedicated)) + (= (prefix-numeric-value current-prefix-arg) 4)) + (list (python-shell-parse-command) nil t))) + (python-shell-make-comint + cmd (python-shell-get-process-name dedicated) show) dedicated) (defun run-python-internal () @@ -1611,7 +1615,6 @@ with user shells. Runs the hook `inferior-python-mode-hook' (after the `comint-mode-hook' is run). \(Type \\[describe-mode] in the process buffer for a list of commands.)" - (interactive) (set-process-query-on-exit-flag (get-buffer-process (python-shell-make-comint @@ -1638,7 +1641,7 @@ of commands.)" (global-proc-buffer-name (format "*%s*" global-proc-name)) (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) (global-running (comint-check-proc global-proc-buffer-name)) - (current-prefix-arg 4)) + (current-prefix-arg 16)) (when (and (not dedicated-running) (not global-running)) (if (call-interactively 'run-python) (setq dedicated-running t) -- cgit v1.2.1 From aa81af715cbca7c85ffe38dd7eebef179be369d7 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Fri, 27 Jul 2012 13:42:19 -0300 Subject: Consistent completion in inferior python with emacs -nw. * progmodes/python.el (inferior-python-mode): replace "" binding in inferior-python-mode-map with "\t". (python-shell-completion-complete-at-point) (python-completion-complete-at-point): Remove interactive spec. --- lisp/progmodes/python.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ab364a5318a..4617ecc420d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1545,7 +1545,7 @@ variable. 'python-shell-completion-complete-at-point nil 'local) (add-to-list (make-local-variable 'comint-dynamic-complete-functions) 'python-shell-completion-complete-at-point) - (define-key inferior-python-mode-map (kbd "") + (define-key inferior-python-mode-map "\t" 'python-shell-completion-complete-or-indent) (when python-shell-enable-font-lock (set (make-local-variable 'font-lock-defaults) @@ -1948,7 +1948,6 @@ completions on the current context." (defun python-shell-completion-complete-at-point () "Perform completion at point in inferior Python process." - (interactive) (and comint-last-prompt-overlay (> (point-marker) (overlay-end comint-last-prompt-overlay)) (python-shell-completion--do-completion-at-point @@ -2063,7 +2062,6 @@ Argument OUTPUT is a string with the output from the comint process." For this to work the best as possible you should call `python-shell-send-buffer' from time to time so context in inferior python process is updated properly." - (interactive) (let ((process (python-shell-get-process))) (if (not process) (error "Completion needs an inferior Python process running") -- cgit v1.2.1 From d77149619bc8407dff2532e9810348ef686d1e28 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 31 Jul 2012 00:31:10 -0300 Subject: * progmodes/python.el (run-python-internal): Disable font lock for internal shells. --- lisp/progmodes/python.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 4617ecc420d..001c28a00fa 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1615,11 +1615,12 @@ with user shells. Runs the hook `inferior-python-mode-hook' (after the `comint-mode-hook' is run). \(Type \\[describe-mode] in the process buffer for a list of commands.)" - (set-process-query-on-exit-flag - (get-buffer-process - (python-shell-make-comint - (python-shell-parse-command) - (python-shell-internal-get-process-name))) nil)) + (let ((python-shell-enable-font-lock nil)) + (set-process-query-on-exit-flag + (get-buffer-process + (python-shell-make-comint + (python-shell-parse-command) + (python-shell-internal-get-process-name))) nil))) (defun python-shell-get-process () "Get inferior Python process for current buffer and return it." -- cgit v1.2.1 From 0d49da68b9cdc70a1f3ced0cae0b480ace5e28fc Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 31 Jul 2012 20:43:31 -0300 Subject: Shell processes: enhancements to startup and CEDET compatibility. * progmodes/python.el (python-shell-send-setup-max-wait): Delete var. (python-shell-make-comint): accept-process-output at startup. (run-python-internal): Set inferior-python-mode-hook to nil. (python-shell-internal-get-or-create-process): call sit-for. (python-preoutput-result): Add obsolete alias. (python-shell-internal-send-string): Use it. (python-shell-send-setup-code): Remove call to accept-process-output. --- lisp/progmodes/python.el | 69 ++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 28 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 001c28a00fa..f946509d6e0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1354,14 +1354,6 @@ Restart the python shell after changing this variable for it to take effect." :group 'python :safe 'booleanp) -(defcustom python-shell-send-setup-max-wait 5 - "Seconds to wait for process output before code setup. -If output is received before the specified time then control is -returned in that moment and not after waiting." - :type 'integer - :group 'python - :safe 'integerp) - (defcustom python-shell-process-environment nil "List of environment variables for Python shell. This variable follows the same rules as `process-environment' @@ -1571,7 +1563,8 @@ non-nil the buffer is shown." (current-buffer (current-buffer))) (with-current-buffer buffer (inferior-python-mode) - (python-util-clone-local-variables current-buffer)))) + (python-util-clone-local-variables current-buffer)) + (accept-process-output (get-buffer-process buffer)))) (and pop (pop-to-buffer proc-buffer-name t)) proc-buffer-name))) @@ -1605,17 +1598,19 @@ process buffer for a list of commands.)" "Run an inferior Internal Python process. Input and output via buffer named after `python-shell-internal-buffer-name' and what -`python-shell-internal-get-process-name' returns. This new kind -of shell is intended to be used for generic communication related -to defined configurations. The main difference with global or -dedicated shells is that these ones are attached to a -configuration, not a buffer. This means that can be used for -example to retrieve the sys.path and other stuff, without messing -with user shells. Runs the hook -`inferior-python-mode-hook' (after the `comint-mode-hook' is -run). \(Type \\[describe-mode] in the process buffer for a list -of commands.)" - (let ((python-shell-enable-font-lock nil)) +`python-shell-internal-get-process-name' returns. + +This new kind of shell is intended to be used for generic +communication related to defined configurations, the main +difference with global or dedicated shells is that these ones are +attached to a configuration, not a buffer. This means that can +be used for example to retrieve the sys.path and other stuff, +without messing with user shells. Note that +`python-shell-enable-font-lock' and `inferior-python-mode-hook' +are set to nil for these shells, so setup codes are not sent at +startup." + (let ((python-shell-enable-font-lock nil) + (inferior-python-mode-hook nil)) (set-process-query-on-exit-flag (get-buffer-process (python-shell-make-comint @@ -1658,12 +1653,25 @@ This is really not necessary at all for the code to work but it's there for compatibility with CEDET.") (make-variable-buffer-local 'python-shell-internal-buffer) +(defvar python-shell-internal-last-output nil + "Last output captured by the internal shell. +This is really not necessary at all for the code to work but it's +there for compatibility with CEDET.") +(make-variable-buffer-local 'python-shell-internal-last-output) + (defun python-shell-internal-get-or-create-process () "Get or create an inferior Internal Python process." (let* ((proc-name (python-shell-internal-get-process-name)) (proc-buffer-name (format "*%s*" proc-name))) - (run-python-internal) - (setq python-shell-internal-buffer proc-buffer-name) + (when (not (process-live-p proc-name)) + (run-python-internal) + (setq python-shell-internal-buffer proc-buffer-name) + ;; XXX: Why is this `sit-for' needed? + ;; `python-shell-make-comint' calls `accept-process-output' + ;; already but it is not helping to get proper output on + ;; 'gnu/linux when the internal shell process is not running and + ;; a call to `python-shell-internal-send-string' is issued. + (sit-for 0.1 t)) (get-buffer-process proc-buffer-name))) (define-obsolete-function-alias @@ -1672,6 +1680,9 @@ there for compatibility with CEDET.") (define-obsolete-variable-alias 'python-buffer 'python-shell-internal-buffer "24.2") +(define-obsolete-variable-alias + 'python-preoutput-result 'python-shell-internal-last-output "24.2") + (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. When MSG is non-nil messages the first line of STRING." @@ -1723,11 +1734,14 @@ the output." (defun python-shell-internal-send-string (string) "Send STRING to the Internal Python interpreter. Returns the output. See `python-shell-send-string-no-output'." - (python-shell-send-string-no-output - ;; Makes this function compatible with the old - ;; python-send-receive. (At least for CEDET). - (replace-regexp-in-string "_emacs_out +" "" string) - (python-shell-internal-get-or-create-process) nil)) + ;; XXX Remove `python-shell-internal-last-output' once CEDET is + ;; updated to support this new mode. + (setq python-shell-internal-last-output + (python-shell-send-string-no-output + ;; Makes this function compatible with the old + ;; python-send-receive. (At least for CEDET). + (replace-regexp-in-string "_emacs_out +" "" string) + (python-shell-internal-get-or-create-process) nil))) (define-obsolete-function-alias 'python-send-receive 'python-shell-internal-send-string "24.2") @@ -1808,7 +1822,6 @@ This function takes the list of setup code to send from the `python-shell-setup-codes' list." (let ((msg "Sent %s") (process (get-buffer-process (current-buffer)))) - (accept-process-output process python-shell-send-setup-max-wait) (dolist (code python-shell-setup-codes) (when code (message (format msg code)) -- cgit v1.2.1 From ba7b015436fce401af7f40d1ee8dcc08cdf2541a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 6 Aug 2012 00:27:43 -0300 Subject: Make internal shell process buffer names start with space. * progmodes/python.el (python-shell-make-comint): Add optional argument INTERNAL. (run-python-internal): Use it. (python-shell-internal-get-or-create-process): Check for new internal buffer names. --- lisp/progmodes/python.el | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f946509d6e0..01c17a70f98 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1546,26 +1546,34 @@ variable. python-syntax-propertize-function)) (compilation-shell-minor-mode 1)) -(defun python-shell-make-comint (cmd proc-name &optional pop) +(defun python-shell-make-comint (cmd proc-name &optional pop internal) "Create a python shell comint buffer. CMD is the python command to be executed and PROC-NAME is the process name the comint buffer will get. After the comint buffer -is created the `inferior-python-mode' is activated. If POP is -non-nil the buffer is shown." +is created the `inferior-python-mode' is activated. When +optional argument POP is non-nil the buffer is shown. When +optional argument INTERNAL is non-nil this process is run on a +buffer with a name that starts with a space, following the Emacs +convention for temporary/internal buffers, and also makes sure +the user is not queried for confirmation when the process is +killed." (save-excursion - (let* ((proc-buffer-name (format "*%s*" proc-name)) + (let* ((proc-buffer-name + (format (if (not internal) "*%s*" " *%s*") proc-name)) (process-environment (python-shell-calculate-process-environment)) (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let* ((cmdlist (split-string-and-unquote cmd)) - (buffer (apply 'make-comint proc-name (car cmdlist) nil - (cdr cmdlist))) - (current-buffer (current-buffer))) + (buffer (apply #'make-comint-in-buffer proc-name proc-buffer-name + (car cmdlist) nil (cdr cmdlist))) + (current-buffer (current-buffer)) + (process (get-buffer-process buffer))) (with-current-buffer buffer (inferior-python-mode) (python-util-clone-local-variables current-buffer)) - (accept-process-output (get-buffer-process buffer)))) - (and pop (pop-to-buffer proc-buffer-name t)) + (accept-process-output process) + (and pop (pop-to-buffer buffer t)) + (and internal (set-process-query-on-exit-flag process nil)))) proc-buffer-name))) ;;;###autoload @@ -1611,11 +1619,10 @@ are set to nil for these shells, so setup codes are not sent at startup." (let ((python-shell-enable-font-lock nil) (inferior-python-mode-hook nil)) - (set-process-query-on-exit-flag - (get-buffer-process - (python-shell-make-comint - (python-shell-parse-command) - (python-shell-internal-get-process-name))) nil))) + (get-buffer-process + (python-shell-make-comint + (python-shell-parse-command) + (python-shell-internal-get-process-name) nil t)))) (defun python-shell-get-process () "Get inferior Python process for current buffer and return it." @@ -1662,7 +1669,7 @@ there for compatibility with CEDET.") (defun python-shell-internal-get-or-create-process () "Get or create an inferior Internal Python process." (let* ((proc-name (python-shell-internal-get-process-name)) - (proc-buffer-name (format "*%s*" proc-name))) + (proc-buffer-name (format " *%s*" proc-name))) (when (not (process-live-p proc-name)) (run-python-internal) (setq python-shell-internal-buffer proc-buffer-name) -- cgit v1.2.1 From 2d79ec42a2955c3c9ecce804576c624d75e08e19 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 7 Aug 2012 23:30:08 -0300 Subject: * progmodes/python.el Fixed defsubst warning. (python-syntax-context) Rename from python-info-ppss-context. (python-syntax-context-type): Rename from python-info-ppss-context-type. (python-syntax-comment-or-string-p): Rename from python-info-ppss-comment-or-string-p. --- lisp/progmodes/python.el | 152 ++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 73 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 01c17a70f98..58d984f8d4d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -353,6 +353,40 @@ This variant of `rx' supports common python named REGEXPS." ;;; Font-lock and syntax + +(defun python-syntax-context (type &optional syntax-ppss) + "Return non-nil if point is on TYPE using SYNTAX-PPSS. +TYPE can be `comment', `string' or `paren'. It returns the start +character address of the specified TYPE." + (let ((ppss (or syntax-ppss (syntax-ppss)))) + (case type + (comment (and (nth 4 ppss) (nth 8 ppss))) + (string (and (not (nth 4 ppss)) (nth 8 ppss))) + (paren (nth 1 ppss)) + (t nil)))) + +(defun python-syntax-context-type (&optional syntax-ppss) + "Return the context type using SYNTAX-PPSS. +The type returned can be `comment', `string' or `paren'." + (let ((ppss (or syntax-ppss (syntax-ppss)))) + (cond + ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string)) + ((nth 1 ppss) 'paren)))) + +(defsubst python-syntax-comment-or-string-p () + "Return non-nil if point is inside 'comment or 'string." + (nth 8 (syntax-ppss))) + +(define-obsolete-function-alias + 'python-info-ppss-context #'python-syntax-context "24.2") + +(define-obsolete-function-alias + 'python-info-ppss-context-type #'python-syntax-context-type "24.2") + +(define-obsolete-function-alias + 'python-info-ppss-comment-or-string-p + #'python-syntax-comment-or-string-p "24.2") + (defvar python-font-lock-keywords ;; Keywords `(,(rx symbol-start @@ -439,9 +473,9 @@ This variant of `rx' supports common python named REGEXPS." (? ?\[ (+ (not (any ?\]))) ?\]) (* space) assignment-operator))) (when (re-search-forward re limit t) - (while (and (python-info-ppss-context 'paren) + (while (and (python-syntax-context 'paren) (re-search-forward re limit t))) - (if (and (not (python-info-ppss-context 'paren)) + (if (and (not (python-syntax-context 'paren)) (not (equal (char-after (point-marker)) ?=))) t (set-match-data nil))))) @@ -454,10 +488,10 @@ This variant of `rx' supports common python named REGEXPS." assignment-operator))) (when (and (re-search-forward re limit t) (goto-char (nth 3 (match-data)))) - (while (and (python-info-ppss-context 'paren) + (while (and (python-syntax-context 'paren) (re-search-forward re limit t)) (goto-char (nth 3 (match-data)))) - (if (not (python-info-ppss-context 'paren)) + (if (not (python-syntax-context 'paren)) t (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)))) @@ -582,7 +616,7 @@ These make `python-indent-calculate-indentation' subtract the value of (re-search-forward (python-rx line-start block-start) nil t)) (when (and - (not (python-info-ppss-context-type)) + (not (python-syntax-context-type)) (progn (goto-char (line-end-position)) (python-util-forward-comment -1) @@ -632,14 +666,14 @@ START is the buffer position where the sexp starts." (bobp)) 'no-indent) ;; Inside a paren - ((setq start (python-info-ppss-context 'paren ppss)) + ((setq start (python-syntax-context 'paren ppss)) 'inside-paren) ;; Inside string - ((setq start (python-info-ppss-context 'string ppss)) + ((setq start (python-syntax-context 'string ppss)) 'inside-string) ;; After backslash - ((setq start (when (not (or (python-info-ppss-context 'string ppss) - (python-info-ppss-context 'comment ppss))) + ((setq start (when (not (or (python-syntax-context 'string ppss) + (python-syntax-context 'comment ppss))) (let ((line-beg-pos (line-beginning-position))) (when (python-info-line-ends-backslash-p (1- line-beg-pos)) @@ -657,7 +691,7 @@ START is the buffer position where the sexp starts." (while (and (re-search-backward (python-rx block-start) nil t) (or - (python-info-ppss-context-type) + (python-syntax-context-type) (python-info-continuation-line-p)))) (when (looking-at (python-rx block-start)) (point-marker))))) @@ -721,13 +755,13 @@ START is the buffer position where the sexp starts." (while (prog2 (forward-line -1) (and (not (bobp)) - (python-info-ppss-context 'paren)))) + (python-syntax-context 'paren)))) (goto-char (line-end-position)) (while (and (re-search-backward "\\." (line-beginning-position) t) - (python-info-ppss-context-type))) + (python-syntax-context-type))) (if (and (looking-at "\\.") - (not (python-info-ppss-context-type))) + (not (python-syntax-context-type))) ;; The indentation is the same column of the ;; first matching dot that's not inside a ;; comment, a string or a paren @@ -783,7 +817,7 @@ START is the buffer position where the sexp starts." (when (and (looking-at (regexp-opt '(")" "]" "}"))) (progn (forward-char 1) - (not (python-info-ppss-context 'paren)))) + (not (python-syntax-context 'paren)))) (goto-char context-start) (current-indentation)))) ;; If open paren is contained on a line by itself add another @@ -883,7 +917,7 @@ See `python-indent-line' for details." (defun python-indent-dedent-line () "De-indent current line." (interactive "*") - (when (and (not (python-info-ppss-comment-or-string-p)) + (when (and (not (python-syntax-comment-or-string-p)) (<= (point-marker) (save-excursion (back-to-indentation) (point-marker))) @@ -974,7 +1008,7 @@ With numeric ARG, just insert that many colons. With (when (and (not arg) (eolp) (not (equal ?: (char-after (- (point-marker) 2)))) - (not (python-info-ppss-comment-or-string-p))) + (not (python-syntax-comment-or-string-p))) (let ((indentation (current-indentation)) (calculated-indentation (python-indent-calculate-indentation))) (python-info-closing-block-message) @@ -998,7 +1032,7 @@ automatically if needed." (goto-char (line-beginning-position)) ;; If after going to the beginning of line the point ;; is still inside a paren it's ok to do the trick - (when (python-info-ppss-context 'paren) + (when (python-syntax-context 'paren) (let ((indentation (python-indent-calculate-indentation))) (when (< (current-indentation) indentation) (indent-line-to indentation))))))) @@ -1032,7 +1066,7 @@ non-nil if point is moved to `beginning-of-defun'." (end-of-line 1)) (while (and (funcall re-search-fn python-nav-beginning-of-defun-regexp nil t) - (python-info-ppss-context-type))) + (python-syntax-context-type))) (and (python-info-looking-at-beginning-of-defun) (or (not (= (line-number-at-pos pos) (line-number-at-pos))) @@ -1082,15 +1116,15 @@ Returns nil if point is not in a def or class." (equal (char-after (+ (point) (current-indentation))) ?#) (<= (current-indentation) beg-defun-indent) (looking-at (python-rx decorator)) - (python-info-ppss-context-type)))) + (python-syntax-context-type)))) (forward-line 1) ;; If point falls inside a paren or string context the point is ;; forwarded at the end of it (or end of buffer if its not closed) - (let ((context-type (python-info-ppss-context-type))) + (let ((context-type (python-syntax-context-type))) (when (memq context-type '(paren string)) ;; Slow but safe. (while (and (not (eobp)) - (python-info-ppss-context-type)) + (python-syntax-context-type)) (forward-line 1))))))) (defun python-nav-beginning-of-statement () @@ -1102,8 +1136,8 @@ Returns nil if point is not in a def or class." (save-excursion (forward-line -1) (python-info-line-ends-backslash-p)) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)) + (python-syntax-context 'string) + (python-syntax-context 'paren)) (forward-line -1))))) (defun python-nav-end-of-statement () @@ -1113,8 +1147,8 @@ Returns nil if point is not in a def or class." (not (eobp)) (when (or (python-info-line-ends-backslash-p) - (python-info-ppss-context 'string) - (python-info-ppss-context 'paren)) + (python-syntax-context 'string) + (python-syntax-context 'paren)) (forward-line 1))))) (defun python-nav-backward-statement (&optional arg) @@ -1202,13 +1236,13 @@ backward to previous block." (python-nav-end-of-statement) (while (and (re-search-forward block-start-regexp nil t) - (python-info-ppss-context-type))) + (python-syntax-context-type))) (setq arg (1- arg))) (while (< arg 0) (python-nav-beginning-of-statement) (while (and (re-search-backward block-start-regexp nil t) - (python-info-ppss-context-type))) + (python-syntax-context-type))) (setq arg (1+ arg))) (python-nav-beginning-of-statement) (if (not (looking-at (python-rx block-start))) @@ -1920,7 +1954,7 @@ completions on the current context." (while (or ;; honor initial paren depth (> (car (syntax-ppss)) paren-depth) - (python-info-ppss-context 'string)) + (python-syntax-context 'string)) (forward-char -1)))) (point))) (end (point)) @@ -2140,7 +2174,7 @@ Optional argument JUSTIFY defines if the paragraph should be justified." ((funcall python-fill-comment-function justify)) ;; Strings/Docstrings ((save-excursion (skip-chars-forward "\"'uUrR") - (python-info-ppss-context 'string)) + (python-syntax-context 'string)) (funcall python-fill-string-function justify)) ;; Decorators ((equal (char-after (save-excursion @@ -2148,7 +2182,7 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (point-marker))) ?@) (funcall python-fill-decorator-function justify)) ;; Parens - ((or (python-info-ppss-context 'paren) + ((or (python-syntax-context 'paren) (looking-at (python-rx open-paren)) (save-excursion (skip-syntax-forward "^(" (line-end-position)) @@ -2168,13 +2202,13 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." (string-start-marker (progn (skip-chars-forward "\"'uUrR") - (goto-char (python-info-ppss-context 'string)) + (goto-char (python-syntax-context 'string)) (skip-chars-forward "\"'uUrR") (point-marker))) (reg-start (line-beginning-position)) (string-end-marker (progn - (while (python-info-ppss-context 'string) + (while (python-syntax-context 'string) (goto-char (1+ (point-marker)))) (skip-chars-backward "\"'") (point-marker))) @@ -2212,16 +2246,16 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." JUSTIFY should be used (if applicable) as in `fill-paragraph'." (save-restriction (narrow-to-region (progn - (while (python-info-ppss-context 'paren) + (while (python-syntax-context 'paren) (goto-char (1- (point-marker)))) (point-marker) (line-beginning-position)) (progn - (when (not (python-info-ppss-context 'paren)) + (when (not (python-syntax-context 'paren)) (end-of-line) - (when (not (python-info-ppss-context 'paren)) + (when (not (python-syntax-context 'paren)) (skip-syntax-backward "^)"))) - (while (python-info-ppss-context 'paren) + (while (python-syntax-context 'paren) (goto-char (1+ (point-marker)))) (point-marker))) (let ((paragraph-start "\f\\|[ \t]*$") @@ -2259,7 +2293,7 @@ the if condition." ;; Only expand in code. :enable-function (lambda () (and - (not (python-info-ppss-comment-or-string-p)) + (not (python-syntax-comment-or-string-p)) python-skeleton-autoinsert))) (defmacro python-skeleton-define (name doc &rest skel) @@ -2579,7 +2613,7 @@ not inside a defun." With optional argument REPLACE-SELF convert \"self\" to current parent defun name." (let ((name - (and (not (python-info-ppss-comment-or-string-p)) + (and (not (python-syntax-comment-or-string-p)) (with-syntax-table python-dotty-syntax-table (let ((sym (symbol-at-point))) (and sym @@ -2657,7 +2691,7 @@ With optional argument LINE-NUMBER, check that line instead." (goto-char line-number)) (while (and (not (eobp)) (goto-char (line-end-position)) - (python-info-ppss-context 'paren) + (python-syntax-context 'paren) (not (equal (char-before (point)) ?\\))) (forward-line 1)) (when (equal (char-before) ?\\) @@ -2674,7 +2708,7 @@ Optional argument LINE-NUMBER forces the line number to check against." (when (python-info-line-ends-backslash-p) (while (save-excursion (goto-char (line-beginning-position)) - (python-info-ppss-context 'paren)) + (python-syntax-context 'paren)) (forward-line -1)) (back-to-indentation) (point-marker))))) @@ -2688,10 +2722,10 @@ where the continued line ends." (widen) (let* ((context-type (progn (back-to-indentation) - (python-info-ppss-context-type))) + (python-syntax-context-type))) (line-start (line-number-at-pos)) (context-start (when context-type - (python-info-ppss-context context-type)))) + (python-syntax-context context-type)))) (cond ((equal context-type 'paren) ;; Lines inside a paren are always a continuation line ;; (except the first one). @@ -2736,41 +2770,13 @@ operator." assignment-operator not-simple-operator) (line-end-position) t) - (not (python-info-ppss-context-type)))) + (not (python-syntax-context-type)))) (skip-syntax-forward "\s") (point-marker))))) -(defun python-info-ppss-context (type &optional syntax-ppss) - "Return non-nil if point is on TYPE using SYNTAX-PPSS. -TYPE can be `comment', `string' or `paren'. It returns the start -character address of the specified TYPE." - (let ((ppss (or syntax-ppss (syntax-ppss)))) - (case type - (comment - (and (nth 4 ppss) - (nth 8 ppss))) - (string - (and (not (nth 4 ppss)) - (nth 8 ppss))) - (paren - (nth 1 ppss)) - (t nil)))) - -(defun python-info-ppss-context-type (&optional syntax-ppss) - "Return the context type using SYNTAX-PPSS. -The type returned can be `comment', `string' or `paren'." - (let ((ppss (or syntax-ppss (syntax-ppss)))) - (cond - ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string)) - ((nth 1 ppss) 'paren)))) - -(defsubst python-info-ppss-comment-or-string-p () - "Return non-nil if point is inside 'comment or 'string." - (nth 8 (syntax-ppss))) - (defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." - (and (not (python-info-ppss-context-type (or syntax-ppss (syntax-ppss)))) + (and (not (python-syntax-context-type (or syntax-ppss (syntax-ppss)))) (save-excursion (beginning-of-line 1) (looking-at python-nav-beginning-of-defun-regexp)))) @@ -2816,7 +2822,7 @@ to \"^python-\"." (defun python-util-forward-comment (&optional direction) "Python mode specific version of `forward-comment'. Optional argument DIRECTION defines the direction to move to." - (let ((comment-start (python-info-ppss-context 'comment)) + (let ((comment-start (python-syntax-context 'comment)) (factor (if (< (or direction 0) 0) -99999 99999))) -- cgit v1.2.1 From 489af14fa55c1774367460c3279c0897f0ea9f2c Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 9 Aug 2012 00:30:37 -0300 Subject: * progmodes/python.el: Enhancements to forward-sexp. (python-nav-forward-sexp): Rename from python-nav-forward-sexp-function. (python-nav--forward-sexp, python-nav--backward-sexp): New functions. --- lisp/progmodes/python.el | 239 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 167 insertions(+), 72 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 58d984f8d4d..c65f2a2d595 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -54,7 +54,7 @@ ;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', ;; `python-nav-beginning-of-block' and `python-nav-end-of-block' are ;; included but no bound to any key. At last but not least the -;; specialized `python-nav-forward-sexp-function' allows easy +;; specialized `python-nav-forward-sexp' allows easy ;; navigation between code blocks. ;; Shell interaction: is provided and allows you to execute easily any @@ -1249,83 +1249,178 @@ backward to previous block." (and (goto-char starting-pos) nil) (and (not (= (point) starting-pos)) (point-marker))))) -(defun python-nav-forward-sexp-function (&optional arg) +(defun python-nav-lisp-forward-sexp-safe (&optional arg) + "Safe version of standard `forward-sexp'. +When ARG > 0 move forward, else if ARG is < 0." + (or arg (setq arg 1)) + (let ((forward-sexp-function nil) + (paren-regexp + (if (> arg 0) (python-rx close-paren) (python-rx open-paren))) + (search-fn + (if (> arg 0) #'re-search-forward #'re-search-backward))) + (condition-case nil + (forward-sexp arg) + (error + (while (and (funcall search-fn paren-regexp nil t) + (python-syntax-context 'paren))))))) + +(defun python-nav--forward-sexp () + "Move to forward sexp." + (case (python-syntax-context-type) + (string + ;; Inside of a string, get out of it. + (while (and (re-search-forward "[\"']" nil t) + (python-syntax-context 'string)))) + (comment + ;; Inside of a comment, just move forward. + (python-util-forward-comment)) + (paren + (python-nav-lisp-forward-sexp-safe 1)) + (t + (if (and (not (eobp)) + (= (syntax-class (syntax-after (point))) 4)) + ;; Looking an open-paren + (python-nav-lisp-forward-sexp-safe 1) + (let ((block-starting-pos + (save-excursion (python-nav-beginning-of-block))) + (block-ending-pos + (save-excursion (python-nav-end-of-block))) + (next-block-starting-pos + (save-excursion (python-nav-forward-block)))) + (cond + ((not block-starting-pos) + ;; Not inside a block, move to closest one. + (and next-block-starting-pos + (goto-char next-block-starting-pos))) + ((= (point) block-starting-pos) + ;; Point is at beginning of block + (if (and next-block-starting-pos + (< next-block-starting-pos block-ending-pos)) + ;; Beginning of next block is closer than current's + ;; end, move to it. + (goto-char next-block-starting-pos) + (goto-char block-ending-pos))) + ((= block-ending-pos (point)) + ;; Point is at end of current block + (let ((parent-block-end-pos + (save-excursion + (python-util-forward-comment) + (python-nav-beginning-of-block) + (python-nav-end-of-block)))) + (if (and parent-block-end-pos + (or (not next-block-starting-pos) + (> next-block-starting-pos parent-block-end-pos))) + ;; If the parent block ends before next block + ;; starts move to it. + (goto-char parent-block-end-pos) + (and next-block-starting-pos + (goto-char next-block-starting-pos))))) + (t (python-nav-end-of-block)))))))) + +(defun python-nav--backward-sexp () + "Move to backward sexp." + (case (python-syntax-context-type) + (string + ;; Inside of a string, get out of it. + (while (and (re-search-backward "[\"']" nil t) + (python-syntax-context 'string)))) + (comment + ;; Inside of a comment, just move backward. + (python-util-forward-comment -1)) + (paren + ;; Handle parens like we are lisp. + (python-nav-lisp-forward-sexp-safe -1)) + (t + (let* ((block-starting-pos + (save-excursion (python-nav-beginning-of-block))) + (block-ending-pos + (save-excursion (python-nav-end-of-block))) + (prev-block-ending-pos + (save-excursion (when (python-nav-backward-block) + (python-nav-end-of-block)))) + (prev-block-parent-ending-pos + (save-excursion + (when prev-block-ending-pos + (goto-char prev-block-ending-pos) + (python-util-forward-comment) + (python-nav-beginning-of-block) + (python-nav-end-of-block))))) + (if (and (not (bobp)) + (= (syntax-class (syntax-after (1- (point)))) 5)) + ;; Char before point is a paren closing char, handle it + ;; like we are lisp. + (python-nav-lisp-forward-sexp-safe -1) + (cond + ((not block-ending-pos) + ;; Not in and ending pos, move to end of previous block. + (and (python-nav-backward-block) + (python-nav-end-of-block))) + ((= (point) block-ending-pos) + ;; In ending pos, we need to search backwards for the + ;; closest point looking the list of candidates from here. + (let ((candidates)) + (dolist (name + '(prev-block-parent-ending-pos + prev-block-ending-pos + block-ending-pos + block-starting-pos)) + (when (and (symbol-value name) + (< (symbol-value name) (point))) + (add-to-list 'candidates (symbol-value name)))) + (goto-char (apply 'max candidates)))) + ((> (point) block-ending-pos) + ;; After an ending position, move to it. + (goto-char block-ending-pos)) + ((= (point) block-starting-pos) + ;; On a block starting position. + (if (not (> (point) (or prev-block-ending-pos (point)))) + ;; Point is after the end position of the block that + ;; wraps the current one, just move a block backward. + (python-nav-backward-block) + ;; If we got here we are facing a case like this one: + ;; + ;; try: + ;; return here() + ;; except Exception as e: + ;; + ;; Where point is on the "except" and must move to the + ;; end of "here()". + (goto-char prev-block-ending-pos) + (let ((parent-block-ending-pos + (save-excursion + (python-nav-forward-sexp) + (and (not (looking-at (python-rx block-start))) + (point))))) + (when (and parent-block-ending-pos + (> parent-block-ending-pos prev-block-ending-pos)) + ;; If we got here we are facing a case like this one: + ;; + ;; except ImportError: + ;; if predicate(): + ;; processing() + ;; here() + ;; except AttributeError: + ;; + ;; Where point is on the "except" and must move to + ;; the end of "here()". Without this extra step we'd + ;; just get to the end of processing(). + (goto-char parent-block-ending-pos))))) + (t + (if (and prev-block-ending-pos (< prev-block-ending-pos (point))) + (goto-char prev-block-ending-pos) + (python-nav-beginning-of-block))))))))) + +(defun python-nav-forward-sexp (&optional arg) "Move forward across one block of code. With ARG, do it that many times. Negative arg -N means move backward N times." (interactive "^p") (or arg (setq arg 1)) (while (> arg 0) - (let ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (next-block-starting-pos - (save-excursion (python-nav-forward-block)))) - (cond ((not block-starting-pos) - (python-nav-forward-block)) - ((= (point) block-starting-pos) - (if (or (not next-block-starting-pos) - (< block-ending-pos next-block-starting-pos)) - (python-nav-end-of-block) - (python-nav-forward-block))) - ((= block-ending-pos (point)) - (let ((parent-block-end-pos - (save-excursion - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block)))) - (if (and parent-block-end-pos - (or (not next-block-starting-pos) - (> next-block-starting-pos parent-block-end-pos))) - (goto-char parent-block-end-pos) - (python-nav-forward-block)))) - (t (python-nav-end-of-block)))) - (setq arg (1- arg))) + (python-nav--forward-sexp) + (setq arg (1- arg))) (while (< arg 0) - (let* ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (prev-block-ending-pos - (save-excursion (when (python-nav-backward-block) - (python-nav-end-of-block)))) - (prev-block-parent-ending-pos - (save-excursion - (when prev-block-ending-pos - (goto-char prev-block-ending-pos) - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block))))) - (cond ((not block-ending-pos) - (and (python-nav-backward-block) - (python-nav-end-of-block))) - ((= (point) block-ending-pos) - (let ((candidates)) - (dolist (name - '(prev-block-parent-ending-pos - prev-block-ending-pos - block-ending-pos - block-starting-pos)) - (when (and (symbol-value name) - (< (symbol-value name) (point))) - (add-to-list 'candidates (symbol-value name)))) - (goto-char (apply 'max candidates)))) - ((> (point) block-ending-pos) - (python-nav-end-of-block)) - ((= (point) block-starting-pos) - (if (not (> (point) (or prev-block-ending-pos (point)))) - (python-nav-backward-block) - (goto-char prev-block-ending-pos) - (let ((parent-block-ending-pos - (save-excursion - (python-nav-forward-sexp-function) - (and (not (looking-at (python-rx block-start))) - (point))))) - (when (and parent-block-ending-pos - (> parent-block-ending-pos prev-block-ending-pos)) - (goto-char parent-block-ending-pos))))) - (t (python-nav-beginning-of-block)))) + (python-nav--backward-sexp) (setq arg (1+ arg)))) @@ -2848,7 +2943,7 @@ if that value is non-nil." (set (make-local-variable 'parse-sexp-ignore-comments) t) (set (make-local-variable 'forward-sexp-function) - 'python-nav-forward-sexp-function) + 'python-nav-forward-sexp) (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords nil nil nil nil)) -- cgit v1.2.1 From e0cc4efac7f170e81c9b95f1a616c1595442936e Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 9 Aug 2012 01:08:29 -0300 Subject: * progmodes/python.el (python-pdbtrack-tracked-buffer) (python-pdbtrack-buffers-to-kill, python-shell-internal-buffer) (python-shell-internal-last-output): Use make-local-variable instead of make-variable-buffer-local. --- lisp/progmodes/python.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c65f2a2d595..310bb8d3f66 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1668,6 +1668,9 @@ variable. 'python-shell-completion-complete-at-point) (define-key inferior-python-mode-map "\t" 'python-shell-completion-complete-or-indent) + (make-local-variable 'python-pdbtrack-buffers-to-kill) + (make-local-variable 'python-pdbtrack-tracked-buffer) + (make-local-variable 'python-shell-internal-last-output) (when python-shell-enable-font-lock (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords nil nil nil nil)) @@ -1787,13 +1790,11 @@ startup." "Current internal shell buffer for the current buffer. This is really not necessary at all for the code to work but it's there for compatibility with CEDET.") -(make-variable-buffer-local 'python-shell-internal-buffer) (defvar python-shell-internal-last-output nil "Last output captured by the internal shell. This is really not necessary at all for the code to work but it's there for compatibility with CEDET.") -(make-variable-buffer-local 'python-shell-internal-last-output) (defun python-shell-internal-get-or-create-process () "Get or create an inferior Internal Python process." @@ -2135,11 +2136,9 @@ Used to extract the current line and module being inspected." "Variable containing the value of the current tracked buffer. Never set this variable directly, use `python-pdbtrack-set-tracked-buffer' instead.") -(make-variable-buffer-local 'python-pdbtrack-tracked-buffer) (defvar python-pdbtrack-buffers-to-kill nil "List of buffers to be deleted after tracking finishes.") -(make-variable-buffer-local 'python-pdbtrack-buffers-to-kill) (defun python-pdbtrack-set-tracked-buffer (file-name) "Set the buffer for FILE-NAME as the tracked buffer. @@ -3004,6 +3003,8 @@ if that value is non-nil." (python-skeleton-add-menu-items) + (make-local-variable 'python-shell-internal-buffer) + (when python-indent-guess-indent-offset (python-indent-guess-indent-offset))) -- cgit v1.2.1 From 1530c98e56d331a21ba7ef2c940d1441442ac798 Mon Sep 17 00:00:00 2001 From: Chong Yidong Date: Sat, 11 Aug 2012 00:46:07 +0800 Subject: Remove some unnecessary bindings of same-window-* variables. * lisp/progmodes/python.el (python-shell-get-process-name): Don't mess with same-window-buffer-names. * lisp/eshell/eshell.el (eshell-add-to-window-buffer-names) (eshell-remove-from-window-buffer-names): Make obsolete. (eshell-buffer-name, eshell-unload-hook): Don't use them. (eshell): Just use pop-to-buffer-same-window instead. --- lisp/progmodes/python.el | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 310bb8d3f66..f695d747483 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1549,16 +1549,12 @@ virtualenv." If DEDICATED is t and the variable `buffer-file-name' is non-nil returns a string with the form `python-shell-buffer-name'[variable `buffer-file-name'] else -returns the value of `python-shell-buffer-name'. After -calculating the process name adds the buffer name for the process -in the `same-window-buffer-names' list." +returns the value of `python-shell-buffer-name'." (let ((process-name (if (and dedicated buffer-file-name) (format "%s[%s]" python-shell-buffer-name buffer-file-name) (format "%s" python-shell-buffer-name)))) - (add-to-list 'same-window-buffer-names (purecopy - (format "*%s*" process-name))) process-name)) (defun python-shell-internal-get-process-name () -- cgit v1.2.1 From 5beed58664d830277199c61ef385f7d4383f1f38 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 14 Aug 2012 01:18:41 -0300 Subject: Use `completion-table-dynamic' for completion functions. * progmodes/python.el (python-shell-completion--do-completion-at-point) (python-shell-completion--get-completions): Remove functions. (python-shell-completion-complete-at-point): New function. (python-completion-complete-at-point): Use it. --- lisp/progmodes/python.el | 143 ++++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 71 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f695d747483..ad2286d4b2b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2018,27 +2018,71 @@ and use the following as the value of this variable: :type 'string :group 'python) -(defun python-shell-completion--get-completions (input process completion-code) - "Retrieve available completions for INPUT using PROCESS. -Argument COMPLETION-CODE is the python code used to get -completions on the current context." - (with-current-buffer (process-buffer process) - (let ((completions (python-shell-send-string-no-output - (format completion-code input) process))) - (when (> (length completions) 2) - (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t))))) - -(defun python-shell-completion--do-completion-at-point (process) - "Do completion at point for PROCESS." - (with-syntax-table python-dotty-syntax-table - (let* ((beg - (save-excursion +(defun python-shell-completion-get-completions (process line input) + "Do completion at point for PROCESS. +LINE is used to detect the context on how to complete given +INPUT." + (let* ((prompt + ;; Get the last prompt for the inferior process + ;; buffer. This is used for the completion code selection + ;; heuristic. + (with-current-buffer (process-buffer process) + (buffer-substring-no-properties + (overlay-start comint-last-prompt-overlay) + (overlay-end comint-last-prompt-overlay)))) + (completion-context + ;; Check whether a prompt matches a pdb string, an import + ;; statement or just the standard prompt and use the + ;; correct python-shell-completion-*-code string + (cond ((and (> (length python-shell-completion-pdb-string-code) 0) + (string-match + (concat "^" python-shell-prompt-pdb-regexp) prompt)) + 'pdb) + ((and (> + (length python-shell-completion-module-string-code) 0) + (string-match + (concat "^" python-shell-prompt-regexp) prompt) + (string-match "^[ \t]*\\(from\\|import\\)[ \t]" line)) + 'import) + ((string-match + (concat "^" python-shell-prompt-regexp) prompt) + 'default) + (t nil))) + (completion-code + (case completion-context + (pdb python-shell-completion-pdb-string-code) + (import python-shell-completion-module-string-code) + (default python-shell-completion-string-code) + (t nil))) + (input + (if (eq completion-context 'import) + (replace-regexp-in-string "^[ \t]+" "" line) + input))) + (and completion-code + (> (length input) 0) + (with-current-buffer (process-buffer process) + (let ((completions (python-shell-send-string-no-output + (format completion-code input) process))) + (and (> (length completions) 2) + (split-string completions + "^'\\|^\"\\|;\\|'$\\|\"$" t))))))) + +(defun python-shell-completion-complete-at-point (&optional process) + "Perform completion at point in inferior Python. +Optional argument PROCESS forces completions to be retrieved +using that one instead of current buffer's process." + (setq process (or process (get-buffer-process (current-buffer)))) + (let* ((start + (save-excursion + (with-syntax-table python-dotty-syntax-table (let* ((paren-depth (car (syntax-ppss))) (syntax-string "w_") (syntax-list (string-to-syntax syntax-string))) - ;; Stop scanning for the beginning of the completion subject - ;; after the char before point matches a delimiter - (while (member (car (syntax-after (1- (point)))) syntax-list) + ;; Stop scanning for the beginning of the completion + ;; subject after the char before point matches a + ;; delimiter + (while (member + (car (syntax-after (1- (point)))) syntax-list) (skip-syntax-backward syntax-string) (when (or (equal (char-before) ?\)) (equal (char-before) ?\")) @@ -2047,58 +2091,15 @@ completions on the current context." ;; honor initial paren depth (> (car (syntax-ppss)) paren-depth) (python-syntax-context 'string)) - (forward-char -1)))) - (point))) - (end (point)) - (line (buffer-substring-no-properties (point-at-bol) end)) - (input (buffer-substring-no-properties beg end)) - ;; Get the last prompt for the inferior process buffer. This is - ;; used for the completion code selection heuristic. - (prompt - (with-current-buffer (process-buffer process) - (buffer-substring-no-properties - (overlay-start comint-last-prompt-overlay) - (overlay-end comint-last-prompt-overlay)))) - (completion-context - ;; Check whether a prompt matches a pdb string, an import statement - ;; or just the standard prompt and use the correct - ;; python-shell-completion-*-code string - (cond ((and (> (length python-shell-completion-pdb-string-code) 0) - (string-match - (concat "^" python-shell-prompt-pdb-regexp) prompt)) - 'pdb) - ((and (> - (length python-shell-completion-module-string-code) 0) - (string-match - (concat "^" python-shell-prompt-regexp) prompt) - (string-match "^[ \t]*\\(from\\|import\\)[ \t]" line)) - 'import) - ((string-match - (concat "^" python-shell-prompt-regexp) prompt) - 'default) - (t nil))) - (completion-code - (case completion-context - ('pdb python-shell-completion-pdb-string-code) - ('import python-shell-completion-module-string-code) - ('default python-shell-completion-string-code) - (t nil))) - (input - (if (eq completion-context 'import) - (replace-regexp-in-string "^[ \t]+" "" line) - input)) - (completions - (and completion-code (> (length input) 0) - (python-shell-completion--get-completions - input process completion-code)))) - (list beg end completions)))) - -(defun python-shell-completion-complete-at-point () - "Perform completion at point in inferior Python process." - (and comint-last-prompt-overlay - (> (point-marker) (overlay-end comint-last-prompt-overlay)) - (python-shell-completion--do-completion-at-point - (get-buffer-process (current-buffer))))) + (forward-char -1))) + (point))))) + (end (point))) + (list start end + (completion-table-dynamic + (apply-partially + #'python-shell-completion-get-completions + process (buffer-substring-no-properties + (line-beginning-position) end)))))) (defun python-shell-completion-complete-or-indent () "Complete or indent depending on the context. @@ -2210,7 +2211,7 @@ inferior python process is updated properly." (let ((process (python-shell-get-process))) (if (not process) (error "Completion needs an inferior Python process running") - (python-shell-completion--do-completion-at-point process)))) + (python-shell-completion-complete-at-point process)))) (add-to-list 'debug-ignored-errors "^Completion needs an inferior Python process running.") -- cgit v1.2.1 From 925411b4cec8a3de935431c24bef817d5f3a5f4a Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Tue, 14 Aug 2012 10:39:27 -0300 Subject: * progmodes/python.el (python-shell-send-string): (python-shell-send-setup-code): Do not use `format' with `message'. --- lisp/progmodes/python.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ad2286d4b2b..90908c80f1b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1822,8 +1822,7 @@ When MSG is non-nil messages the first line of STRING." (interactive "sPython command: ") (let ((process (or process (python-shell-get-or-create-process))) (lines (split-string string "\n" t))) - (when msg - (message (format "Sent: %s..." (nth 0 lines)))) + (and msg (message "Sent: %s..." (nth 0 lines))) (if (> (length lines) 1) (let* ((temp-file-name (make-temp-file "py")) (file-name (or (buffer-file-name) temp-file-name))) @@ -1953,11 +1952,10 @@ FILE-NAME." "Send all setup code for shell. This function takes the list of setup code to send from the `python-shell-setup-codes' list." - (let ((msg "Sent %s") - (process (get-buffer-process (current-buffer)))) + (let ((process (get-buffer-process (current-buffer)))) (dolist (code python-shell-setup-codes) (when code - (message (format msg code)) + (message "Sent %s" code) (python-shell-send-string (symbol-value code) process))))) -- cgit v1.2.1 From 2a1e24765bc3de7bf72e7117893307f6f6c441be Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Wed, 15 Aug 2012 09:29:11 -0700 Subject: Replace version 24.2 with 24.3 where appropriate (hopefully) --- lisp/progmodes/python.el | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 90908c80f1b..601850ed0fb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -378,14 +378,14 @@ The type returned can be `comment', `string' or `paren'." (nth 8 (syntax-ppss))) (define-obsolete-function-alias - 'python-info-ppss-context #'python-syntax-context "24.2") + 'python-info-ppss-context #'python-syntax-context "24.3") (define-obsolete-function-alias - 'python-info-ppss-context-type #'python-syntax-context-type "24.2") + 'python-info-ppss-context-type #'python-syntax-context-type "24.3") (define-obsolete-function-alias 'python-info-ppss-comment-or-string-p - #'python-syntax-comment-or-string-p "24.2") + #'python-syntax-comment-or-string-p "24.3") (defvar python-font-lock-keywords ;; Keywords @@ -588,10 +588,10 @@ It makes underscores and dots word constituent chars.") :safe 'booleanp) (define-obsolete-variable-alias - 'python-indent 'python-indent-offset "24.2") + 'python-indent 'python-indent-offset "24.3") (define-obsolete-variable-alias - 'python-guess-indent 'python-indent-guess-indent-offset "24.2") + 'python-guess-indent 'python-indent-guess-indent-offset "24.3") (defvar python-indent-current-level 0 "Current indentation level `python-indent-line-function' is using.") @@ -1808,13 +1808,13 @@ there for compatibility with CEDET.") (get-buffer-process proc-buffer-name))) (define-obsolete-function-alias - 'python-proc 'python-shell-internal-get-or-create-process "24.2") + 'python-proc 'python-shell-internal-get-or-create-process "24.3") (define-obsolete-variable-alias - 'python-buffer 'python-shell-internal-buffer "24.2") + 'python-buffer 'python-shell-internal-buffer "24.3") (define-obsolete-variable-alias - 'python-preoutput-result 'python-shell-internal-last-output "24.2") + 'python-preoutput-result 'python-shell-internal-last-output "24.3") (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. @@ -1876,10 +1876,10 @@ Returns the output. See `python-shell-send-string-no-output'." (python-shell-internal-get-or-create-process) nil))) (define-obsolete-function-alias - 'python-send-receive 'python-shell-internal-send-string "24.2") + 'python-send-receive 'python-shell-internal-send-string "24.3") (define-obsolete-function-alias - 'python-send-string 'python-shell-internal-send-string "24.2") + 'python-send-string 'python-shell-internal-send-string "24.3") (defun python-shell-send-region (start end) "Send the region delimited by START and END to inferior Python process." @@ -2369,7 +2369,7 @@ the if condition." :safe 'booleanp) (define-obsolete-variable-alias - 'python-use-skeletons 'python-skeleton-autoinsert "24.2") + 'python-use-skeletons 'python-skeleton-autoinsert "24.3") (defvar python-skeleton-available '() "Internal list of available skeletons.") -- cgit v1.2.1 From 9dd40b000598aff88c6249ecf2b5a866935e9321 Mon Sep 17 00:00:00 2001 From: Matt McClure Date: Fri, 7 Sep 2012 16:53:15 +0200 Subject: * progmodes/python.el (python-shell-send-string): When default-directory is remote, create temp file on remote filesystem. (python-shell-send-file): When file is remote, pass local view of file paths to remote Python interpreter. (Bug#12340) --- lisp/progmodes/python.el | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 601850ed0fb..ffc6c1ac885 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1824,7 +1824,11 @@ When MSG is non-nil messages the first line of STRING." (lines (split-string string "\n" t))) (and msg (message "Sent: %s..." (nth 0 lines))) (if (> (length lines) 1) - (let* ((temp-file-name (make-temp-file "py")) + (let* ((temporary-file-directory + (if (file-remote-p default-directory) + (concat (file-remote-p default-directory) "/tmp") + temporary-file-directory)) + (temp-file-name (make-temp-file "py")) (file-name (or (buffer-file-name) temp-file-name))) (with-temp-file temp-file-name (insert string) @@ -1931,8 +1935,14 @@ FILE-NAME." (interactive "fFile to send: ") (let* ((process (or process (python-shell-get-or-create-process))) (temp-file-name (when temp-file-name - (expand-file-name temp-file-name))) - (file-name (or (expand-file-name file-name) temp-file-name))) + (expand-file-name + (or (file-remote-p temp-file-name 'localname) + temp-file-name)))) + (file-name (or (when file-name + (expand-file-name + (or (file-remote-p file-name 'localname) + file-name))) + temp-file-name))) (when (not file-name) (error "If FILE-NAME is nil then TEMP-FILE-NAME must be non-nil")) (python-shell-send-string -- cgit v1.2.1 From 095bb82357c1fb173a20518104d56fbeb5135f43 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 23 Sep 2012 15:21:33 -0300 Subject: * progmodes/python.el (python-indent-line): More consistent cursor movement behavior. --- lisp/progmodes/python.el | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ffc6c1ac885..8b8002b84b7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -897,16 +897,27 @@ possible indentation levels and saves it in the variable `python-indent-levels'. Afterwards it sets the variable `python-indent-current-level' correctly so offset is equal to (`nth' `python-indent-current-level' `python-indent-levels')" - (if (or (and (eq this-command 'indent-for-tab-command) - (eq last-command this-command)) - force-toggle) - (if (not (equal python-indent-levels '(0))) - (python-indent-toggle-levels) - (python-indent-calculate-levels)) - (python-indent-calculate-levels)) - (beginning-of-line) - (delete-horizontal-space) - (indent-to (nth python-indent-current-level python-indent-levels)) + (or + (and (or (and (eq this-command 'indent-for-tab-command) + (eq last-command this-command)) + force-toggle) + (not (equal python-indent-levels '(0))) + (or (python-indent-toggle-levels) t)) + (python-indent-calculate-levels)) + (let* ((starting-pos (point-marker)) + (indent-ending-position + (+ (line-beginning-position) (current-indentation))) + (follow-indentation-p + (or (bolp) + (and (<= (line-beginning-position) starting-pos) + (>= indent-ending-position starting-pos)))) + (next-indent (nth python-indent-current-level python-indent-levels))) + (unless (= next-indent (current-indentation)) + (beginning-of-line) + (delete-horizontal-space) + (indent-to next-indent) + (goto-char starting-pos)) + (and follow-indentation-p (back-to-indentation))) (python-info-closing-block-message)) (defun python-indent-line-function () -- cgit v1.2.1 From 8fb8b88f2d1ac6dd0feb3c590614257f8fad0fee Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 24 Sep 2012 14:54:46 -0300 Subject: Enhancements for triple-quote string syntax. * progmodes/python.el (python-quote-syntax): Remove. (python-syntax-propertize-function): New value. (python-syntax-count-quotes, python-syntax-stringify): New functions. --- lisp/progmodes/python.el | 106 +++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 45 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8b8002b84b7..cc835ca79ac 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -497,52 +497,68 @@ The type returned can be `comment', `string' or `paren'." (1 font-lock-variable-name-face nil nil)))) (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)))))) - -(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 "|")))) - ))) + ((rx + ;; Match even number of backslashes. + (or (not (any ?\\ ?\' ?\")) point) (* ?\\ ?\\) + ;; Match single or triple quotes of any kind. + (group (or "\"" "\"\"\"" "'" "'''"))) + (1 (ignore (python-syntax-stringify)))) + ((rx + ;; Match odd number of backslashes. + (or (not (any ?\\)) point) ?\\ (* ?\\ ?\\) + ;; Followed by even number of equal quotes. + (group (or "\"\"" "\"\"\"\"" "''" "''''"))) + (1 (ignore (python-syntax-stringify)))))) + +(defsubst python-syntax-count-quotes (quote-char &optional point limit) + "Count number of quotes around point (max is 3). +QUOTE-CHAR is the quote char to count. Optional argument POINT is +the point where scan starts (defaults to current point) and LIMIT +is used to limit the scan." + (let ((i 0)) + (while (and (< i 3) + (or (not limit) (< (+ point i) limit)) + (eq (char-after (+ point i)) quote-char)) + (incf i)) + i)) + +(defun python-syntax-stringify () + "Put `syntax-table' property correctly on single/triple quotes." + (let* ((num-quotes + (let ((n (length (match-string-no-properties 1)))) + ;; This corrects the quote count when matching odd number + ;; of backslashes followed by even number of quotes. + (or (and (= 1 (logand n 1)) n) (1- n)))) + (ppss (prog2 + (backward-char num-quotes) + (syntax-ppss) + (forward-char num-quotes))) + (string-start (and (not (nth 4 ppss)) (nth 8 ppss))) + (quote-starting-pos (- (point) num-quotes)) + (quote-ending-pos (point)) + (num-closing-quotes + (and string-start + (python-syntax-count-quotes + (char-before) string-start quote-starting-pos)))) + (cond ((and string-start (= num-closing-quotes 0)) + ;; This set of quotes doesn't match the string starting + ;; kind. Do nothing. + nil) + ((not string-start) + ;; This set of quotes delimit the start of a string. + (put-text-property quote-starting-pos (1+ quote-starting-pos) + 'syntax-table (string-to-syntax "|"))) + ((= num-quotes num-closing-quotes) + ;; This set of quotes delimit the end of a string. + (put-text-property (1- quote-ending-pos) quote-ending-pos + 'syntax-table (string-to-syntax "|"))) + ((> num-quotes num-closing-quotes) + ;; This may only happen whenever a triple quote is closing + ;; a single quoted string. Add string delimiter syntax to + ;; all three quotes. + (put-text-property quote-starting-pos quote-ending-pos + 'syntax-table (string-to-syntax "|")))))) (defvar python-mode-syntax-table (let ((table (make-syntax-table))) -- cgit v1.2.1 From dc4f818b02a02ec6f93977885e2ff75f8210c14b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 24 Sep 2012 20:15:40 -0300 Subject: * progmodes/python.el: (python-shell-send-buffer): Better handling of "if __name__ == '__main__':" conditionals when sending the buffer. --- lisp/progmodes/python.el | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index cc835ca79ac..e99e6bda4b8 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1919,19 +1919,18 @@ Returns the output. See `python-shell-send-string-no-output'." (defun python-shell-send-buffer (&optional arg) "Send the entire buffer to inferior Python process. - -With prefix ARG include lines surrounded by \"if __name__ == '__main__':\"" +With prefix ARG allow execution of code inside blocks delimited +by \"if __name__== '__main__':\"" (interactive "P") (save-restriction (widen) - (python-shell-send-region - (point-min) - (or (and - (not arg) - (save-excursion - (re-search-forward (python-rx if-name-main) nil t)) - (match-beginning 0)) - (point-max))))) + (let ((str (buffer-substring (point-min) (point-max)))) + (and + (not arg) + (setq str (replace-regexp-in-string + (python-rx if-name-main) + "if __name__ == '__main__ ':" str))) + (python-shell-send-string str)))) (defun python-shell-send-defun (arg) "Send the current defun to inferior Python process. -- cgit v1.2.1 From 5727eadff9e9bfd5d0af90e4fbe21697cba444b1 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 30 Sep 2012 17:14:02 -0300 Subject: Enhancements for triple-quote string syntax. * progmodes/python.el (python-syntax-propertize-function): Match both quote cases in one regexp. (python-syntax-stringify): Handle matches properly. --- lisp/progmodes/python.el | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index e99e6bda4b8..c5ba9a6aecb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -499,17 +499,17 @@ The type returned can be `comment', `string' or `paren'." (defconst python-syntax-propertize-function (syntax-propertize-rules ((rx - ;; Match even number of backslashes. - (or (not (any ?\\ ?\' ?\")) point) (* ?\\ ?\\) - ;; Match single or triple quotes of any kind. - (group (or "\"" "\"\"\"" "'" "'''"))) - (1 (ignore (python-syntax-stringify)))) - ((rx - ;; Match odd number of backslashes. - (or (not (any ?\\)) point) ?\\ (* ?\\ ?\\) - ;; Followed by even number of equal quotes. - (group (or "\"\"" "\"\"\"\"" "''" "''''"))) - (1 (ignore (python-syntax-stringify)))))) + (or (and + ;; Match even number of backslashes. + (or (not (any ?\\ ?\' ?\")) point) (* ?\\ ?\\) + ;; Match single or triple quotes of any kind. + (group (or "\"" "\"\"\"" "'" "'''"))) + (and + ;; Match odd number of backslashes. + (or (not (any ?\\)) point) ?\\ (* ?\\ ?\\) + ;; Followed by even number of equal quotes. + (group (or "\"\"" "\"\"\"\"" "''" "''''"))))) + (0 (ignore (python-syntax-stringify)))))) (defsubst python-syntax-count-quotes (quote-char &optional point limit) "Count number of quotes around point (max is 3). @@ -526,7 +526,8 @@ is used to limit the scan." (defun python-syntax-stringify () "Put `syntax-table' property correctly on single/triple quotes." (let* ((num-quotes - (let ((n (length (match-string-no-properties 1)))) + (let ((n (length (or (match-string-no-properties 1) + (match-string-no-properties 2))))) ;; This corrects the quote count when matching odd number ;; of backslashes followed by even number of quotes. (or (and (= 1 (logand n 1)) n) (1- n)))) -- cgit v1.2.1 From 0478776bb7f04bfb60ff7a2397546c955a1f3ce2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 30 Sep 2012 21:53:44 -0300 Subject: Shell output catching a la gud-gdb. * progmodes/python.el (python-shell-fetch-lines-in-progress) (python-shell-fetch-lines-string, python-shell-fetched-lines): New Vars. (python-shell-fetch-lines-filter): New function. (python-shell-send-string-no-output): Use them. --- lisp/progmodes/python.el | 50 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 18 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c5ba9a6aecb..26758105218 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1867,31 +1867,45 @@ When MSG is non-nil messages the first line of STRING." (string-match "\n[ \t].*\n?$" string)) (comint-send-string process "\n"))))) +;; Shell output catching stolen from gud-gdb +(defvar python-shell-fetch-lines-in-progress nil) +(defvar python-shell-fetch-lines-string nil) +(defvar python-shell-fetched-lines nil) + +(defun python-shell-fetch-lines-filter (string) + "Filter used to read the list of lines output by a command. +STRING is the output to filter." + (setq string (concat python-shell-fetch-lines-string string)) + (while (string-match "\n" string) + (push (substring string 0 (match-beginning 0)) + python-shell-fetched-lines) + (setq string (substring string (match-end 0)))) + (if (equal (string-match comint-prompt-regexp string) 0) + (progn + (setq python-shell-fetch-lines-in-progress nil) + string) + (progn + (setq python-shell-fetch-lines-string string) + ""))) + (defun python-shell-send-string-no-output (string &optional process msg) "Send STRING to PROCESS and inhibit output. When MSG is non-nil messages the first line of STRING. Return the output." - (let* ((output-buffer "") - (process (or process (python-shell-get-or-create-process))) - (comint-preoutput-filter-functions - (append comint-preoutput-filter-functions - '(ansi-color-filter-apply - (lambda (string) - (setq output-buffer (concat output-buffer string)) - "")))) - (inhibit-quit t)) + (let ((process (or process (python-shell-get-or-create-process))) + (comint-preoutput-filter-functions + '(python-shell-fetch-lines-filter)) + (python-shell-fetch-lines-in-progress t) + (inhibit-quit t)) (or (with-local-quit (python-shell-send-string string process msg) - (accept-process-output process) - (replace-regexp-in-string - (if (> (length python-shell-prompt-output-regexp) 0) - (format "\n*%s$\\|^%s\\|\n$" - python-shell-prompt-regexp - (or python-shell-prompt-output-regexp "")) - (format "\n*$\\|^%s\\|\n$" - python-shell-prompt-regexp)) - "" output-buffer)) + (while python-shell-fetch-lines-in-progress + (accept-process-output process)) + (prog1 + (mapconcat #'identity + (reverse python-shell-fetched-lines) "\n") + (setq python-shell-fetched-lines nil))) (with-current-buffer (process-buffer process) (comint-interrupt-subjob))))) -- cgit v1.2.1 From a1a9f411ab644cb191442ea1de4bc1370341cc88 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Wed, 3 Oct 2012 18:53:09 -0300 Subject: Fix cornercase for string syntax. * progmodes/python.el (python-syntax-propertize-function): Simplify and enhance the regexp for unescaped quotes. Now it also matches quotes in weird situations like the single quote in "something\"'". (python-syntax-stringify): Simplify num-quotes detecting code. --- lisp/progmodes/python.el | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 26758105218..f5e4bffd598 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -499,16 +499,15 @@ The type returned can be `comment', `string' or `paren'." (defconst python-syntax-propertize-function (syntax-propertize-rules ((rx - (or (and - ;; Match even number of backslashes. - (or (not (any ?\\ ?\' ?\")) point) (* ?\\ ?\\) - ;; Match single or triple quotes of any kind. - (group (or "\"" "\"\"\"" "'" "'''"))) - (and - ;; Match odd number of backslashes. - (or (not (any ?\\)) point) ?\\ (* ?\\ ?\\) - ;; Followed by even number of equal quotes. - (group (or "\"\"" "\"\"\"\"" "''" "''''"))))) + (and + ;; Match even number of backslashes. + (or (not (any ?\\ ?\' ?\")) point + ;; Quotes might be preceeded by a escaped quote. + (and (or (not (any ?\\)) point) ?\\ + (* ?\\ ?\\) (any ?\' ?\"))) + (* ?\\ ?\\) + ;; Match single or triple quotes of any kind. + (group (or "\"" "\"\"\"" "'" "'''")))) (0 (ignore (python-syntax-stringify)))))) (defsubst python-syntax-count-quotes (quote-char &optional point limit) @@ -525,12 +524,7 @@ is used to limit the scan." (defun python-syntax-stringify () "Put `syntax-table' property correctly on single/triple quotes." - (let* ((num-quotes - (let ((n (length (or (match-string-no-properties 1) - (match-string-no-properties 2))))) - ;; This corrects the quote count when matching odd number - ;; of backslashes followed by even number of quotes. - (or (and (= 1 (logand n 1)) n) (1- n)))) + (let* ((num-quotes (length (match-string-no-properties 1))) (ppss (prog2 (backward-char num-quotes) (syntax-ppss) -- cgit v1.2.1 From fc345011a69cd7e3d38d1b6e06f85ef9d7658232 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 4 Oct 2012 13:39:37 -0300 Subject: Allow user to set docstring style for fill-paragraph. * progmodes/python.el (python-fill-comment-function, python-fill-string-function) (python-fill-decorator-function, python-fill-paren-function): Remove :safe for defcustoms. (python-fill-string-style): New defcustom (python-fill-paragraph-function): Enhanced context detection. (python-fill-string): Honor python-fill-string-style settings. --- lisp/progmodes/python.el | 176 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 128 insertions(+), 48 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f5e4bffd598..b917d3f6429 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2267,32 +2267,82 @@ inferior python process is updated properly." This is the function used by `python-fill-paragraph-function' to fill comments." :type 'symbol - :group 'python - :safe 'symbolp) + :group 'python) (defcustom python-fill-string-function 'python-fill-string "Function to fill strings. This is the function used by `python-fill-paragraph-function' to fill strings." :type 'symbol - :group 'python - :safe 'symbolp) + :group 'python) (defcustom python-fill-decorator-function 'python-fill-decorator "Function to fill decorators. This is the function used by `python-fill-paragraph-function' to fill decorators." :type 'symbol - :group 'python - :safe 'symbolp) + :group 'python) (defcustom python-fill-paren-function 'python-fill-paren "Function to fill parens. This is the function used by `python-fill-paragraph-function' to fill parens." :type 'symbol + :group 'python) + +(defcustom python-fill-string-style 'pep-257 + "Style used to fill docstrings. +This affects `python-fill-string' behavior with regards to +triple quotes positioning. + +Possible values are DJANGO, PEP-257, PEP-257-NN, SYMMETRIC and +NIL. A value of NIL won't care about quotes position, will do +what `fill-paragraph' does, any other value may result in one of +the following docstring styles: + +DJANGO: + + \"\"\" + Process foo, return bar. + \"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +PEP-257: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + +PEP-257-NN: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +SYMMETRIC: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\"" + :type 'symbol :group 'python - :safe 'symbolp) + :safe (lambda (val) (memq val '(django pep-257 pep-257-nn symmetric nil)))) (defun python-fill-paragraph-function (&optional justify) "`fill-paragraph-function' handling multi-line strings and possibly comments. @@ -2302,18 +2352,19 @@ the string's indentation. Optional argument JUSTIFY defines if the paragraph should be justified." (interactive "P") (save-excursion - (back-to-indentation) (cond ;; Comments - ((funcall python-fill-comment-function justify)) + ((python-syntax-context 'comment) + (funcall python-fill-comment-function justify)) ;; Strings/Docstrings - ((save-excursion (skip-chars-forward "\"'uUrR") - (python-syntax-context 'string)) + ((save-excursion (or (python-syntax-context 'string) + (equal (string-to-syntax "|") + (syntax-after (point))))) (funcall python-fill-string-function justify)) ;; Decorators ((equal (char-after (save-excursion (back-to-indentation) - (point-marker))) ?@) + (point))) ?@) (funcall python-fill-decorator-function justify)) ;; Parens ((or (python-syntax-context 'paren) @@ -2332,43 +2383,72 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." (defun python-fill-string (&optional justify) "String fill function for `python-fill-paragraph-function'. JUSTIFY should be used (if applicable) as in `fill-paragraph'." - (let ((marker (point-marker)) - (string-start-marker - (progn - (skip-chars-forward "\"'uUrR") - (goto-char (python-syntax-context 'string)) - (skip-chars-forward "\"'uUrR") - (point-marker))) - (reg-start (line-beginning-position)) - (string-end-marker - (progn - (while (python-syntax-context 'string) - (goto-char (1+ (point-marker)))) - (skip-chars-backward "\"'") - (point-marker))) - (reg-end (line-end-position)) - (fill-paragraph-function)) + (let* ((marker (point-marker)) + (str-start-pos + (let ((m (make-marker))) + (setf (marker-position m) + (or (python-syntax-context 'string) + (and (equal (string-to-syntax "|") + (syntax-after (point))) + (point)))) m)) + (num-quotes (python-syntax-count-quotes + (char-after str-start-pos) str-start-pos)) + (str-end-pos + (save-excursion + (goto-char (+ str-start-pos num-quotes)) + (or (re-search-forward (rx (syntax string-delimiter)) nil t) + (goto-char (point-max))) + (point-marker))) + (multi-line-p + ;; Docstring styles may vary for oneliners and multi-liners. + (> (count-matches "\n" str-start-pos str-end-pos) 0)) + (delimiters-style + (case python-fill-string-style + ;; delimiters-style is a cons cell with the form + ;; (START-NEWLINES . END-NEWLINES). When any of the sexps + ;; is NIL means to not add any newlines for start or end + ;; of docstring. See `python-fill-string-style' for a + ;; graphic idea of each style. + (pep-257 (and multi-line-p (cons nil 2))) + (pep-257-nn (and multi-line-p (cons nil 1))) + (django (cons 1 1)) + (symmetric (and multi-line-p (cons 1 1))))) + (docstring-p (save-excursion + ;; Consider docstrings those strings which + ;; start on a line by themselves. + (goto-char str-start-pos) + (skip-chars-backward (rx whitespace)) + (= (point) (line-beginning-position)))) + (fill-paragraph-function)) (save-restriction - (narrow-to-region reg-start reg-end) - (save-excursion - (goto-char string-start-marker) - (delete-region (point-marker) (progn - (skip-syntax-forward "> ") - (point-marker))) - (goto-char string-end-marker) - (delete-region (point-marker) (progn - (skip-syntax-backward "> ") - (point-marker))) - (save-excursion - (goto-char marker) - (fill-paragraph justify)) - ;; If there is a newline in the docstring lets put triple - ;; quote in it's own line to follow pep 8 - (when (save-excursion - (re-search-backward "\n" string-start-marker t)) - (newline) - (newline-and-indent)) - (fill-paragraph justify)))) t) + (narrow-to-region str-start-pos str-end-pos) + (fill-paragraph justify)) + (save-excursion + (when (and docstring-p python-fill-string-style) + ;; Add the number of newlines indicated by the selected style + ;; at the start of the docstring. + (goto-char (+ str-start-pos num-quotes)) + (delete-region (point) (progn + (skip-syntax-forward "> ") + (point))) + (and (car delimiters-style) + (or (newline (car delimiters-style)) t) + ;; Indent only if a newline is added. + (indent-according-to-mode)) + ;; Add the number of newlines indicated by the selected style + ;; at the end of the docstring. + (goto-char (if (not (= str-end-pos (point-max))) + (- str-end-pos num-quotes) + str-end-pos)) + (delete-region (point) (progn + (skip-syntax-backward "> ") + (point))) + (and (cdr delimiters-style) + ;; Add newlines only if string ends. + (not (= str-end-pos (point-max))) + (or (newline (cdr delimiters-style)) t) + ;; Again indent only if a newline is added. + (indent-according-to-mode))))) t) (defun python-fill-decorator (&optional justify) "Decorator fill function for `python-fill-paragraph-function'. -- cgit v1.2.1 From 735135f9bc3c01b91ffef7c418dd9281bc347ab7 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 4 Oct 2012 22:57:24 -0700 Subject: Spelling fixes. --- lisp/progmodes/python.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b917d3f6429..d5aa73f5ef3 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -502,7 +502,7 @@ The type returned can be `comment', `string' or `paren'." (and ;; Match even number of backslashes. (or (not (any ?\\ ?\' ?\")) point - ;; Quotes might be preceeded by a escaped quote. + ;; Quotes might be preceded by a escaped quote. (and (or (not (any ?\\)) point) ?\\ (* ?\\ ?\\) (any ?\' ?\"))) (* ?\\ ?\\) -- cgit v1.2.1 From 7fa36ccb71d0def28bc3f8eb17827fc870bd7ca2 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Fri, 5 Oct 2012 10:42:08 -0300 Subject: Enhancements to docstring formatting when filling paragraphs. * progmodes/python.el (python-fill-docstring-style): Rename from python-fill-string-style. Added new style. (python-fill-string): Use new style. Better checks for docstrings. --- lisp/progmodes/python.el | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index d5aa73f5ef3..b3b3b0181d7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2290,15 +2290,15 @@ fill parens." :type 'symbol :group 'python) -(defcustom python-fill-string-style 'pep-257 +(defcustom python-fill-docstring-style 'pep-257 "Style used to fill docstrings. This affects `python-fill-string' behavior with regards to triple quotes positioning. -Possible values are DJANGO, PEP-257, PEP-257-NN, SYMMETRIC and -NIL. A value of NIL won't care about quotes position, will do -what `fill-paragraph' does, any other value may result in one of -the following docstring styles: +Possible values are DJANGO, ONETWO, PEP-257, PEP-257-NN, +SYMMETRIC, and NIL. A value of NIL won't care about quotes +position and will treat docstrings a normal string, any other +value may result in one of the following docstring styles: DJANGO: @@ -2312,6 +2312,17 @@ DJANGO: If processing fails throw ProcessingError. \"\"\" +ONETWO: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + PEP-257: \"\"\"Process foo, return bar.\"\"\" @@ -2340,9 +2351,16 @@ SYMMETRIC: If processing fails throw ProcessingError. \"\"\"" - :type 'symbol + :type '(choice + (const :tag "Don't format docstrings" nil) + (const :tag "Django's coding standards style." django) + (const :tag "One newline and start and Two at end style." onetwo) + (const :tag "PEP-257 with 2 newlines at end of string." pep-257) + (const :tag "PEP-257 with 1 newline at end of string." pep-257-nn) + (const :tag "Symmetric style." symmetric)) :group 'python - :safe (lambda (val) (memq val '(django pep-257 pep-257-nn symmetric nil)))) + :safe (lambda (val) + (memq val '(django onetwo pep-257 pep-257-nn symmetric nil)))) (defun python-fill-paragraph-function (&optional justify) "`fill-paragraph-function' handling multi-line strings and possibly comments. @@ -2403,28 +2421,28 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." ;; Docstring styles may vary for oneliners and multi-liners. (> (count-matches "\n" str-start-pos str-end-pos) 0)) (delimiters-style - (case python-fill-string-style + (case python-fill-docstring-style ;; delimiters-style is a cons cell with the form ;; (START-NEWLINES . END-NEWLINES). When any of the sexps ;; is NIL means to not add any newlines for start or end - ;; of docstring. See `python-fill-string-style' for a + ;; of docstring. See `python-fill-docstring-style' for a ;; graphic idea of each style. + (django (cons 1 1)) + (onetwo (and multi-line-p (cons 1 2))) (pep-257 (and multi-line-p (cons nil 2))) (pep-257-nn (and multi-line-p (cons nil 1))) - (django (cons 1 1)) (symmetric (and multi-line-p (cons 1 1))))) (docstring-p (save-excursion ;; Consider docstrings those strings which ;; start on a line by themselves. - (goto-char str-start-pos) - (skip-chars-backward (rx whitespace)) - (= (point) (line-beginning-position)))) + (python-nav-beginning-of-statement) + (and (= (point) str-start-pos)))) (fill-paragraph-function)) (save-restriction (narrow-to-region str-start-pos str-end-pos) (fill-paragraph justify)) (save-excursion - (when (and docstring-p python-fill-string-style) + (when (and docstring-p python-fill-docstring-style) ;; Add the number of newlines indicated by the selected style ;; at the start of the docstring. (goto-char (+ str-start-pos num-quotes)) -- cgit v1.2.1 From 08f18c3d4d86ba60c9f31512c310fffd1a60f9eb Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 7 Oct 2012 01:33:16 -0300 Subject: Fix shell output retrieval and comint-prompt-regexp init. * progmodes/python.el (inferior-python-mode): (python-shell-make-comint): Fix initialization of comint-prompt-regexp from copied file local variables. (python-shell-fetched-lines): Remove var. (python-shell-output-filter-in-progress): Rename from python-shell-fetch-lines-in-progress. (python-shell-output-filter-buffer): Rename from python-shell-fetch-lines-string. (python-shell-fetch-lines-filter): Delete function. (python-shell-output-filter): New function. (python-shell-send-string-no-output): Use them. --- lisp/progmodes/python.el | 77 +++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 31 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b3b3b0181d7..726c0b2d542 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1667,10 +1667,6 @@ variable. \(Type \\[describe-mode] in the process buffer for a list of commands.)" (set-syntax-table python-mode-syntax-table) (setq mode-line-process '(":%s")) - (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" - python-shell-prompt-regexp - python-shell-prompt-block-regexp - python-shell-prompt-pdb-regexp)) (make-local-variable 'comint-output-filter-functions) (add-hook 'comint-output-filter-functions 'python-comint-output-filter-function) @@ -1720,7 +1716,11 @@ killed." (process (get-buffer-process buffer))) (with-current-buffer buffer (inferior-python-mode) - (python-util-clone-local-variables current-buffer)) + (python-util-clone-local-variables current-buffer) + (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-pdb-regexp))) (accept-process-output process) (and pop (pop-to-buffer buffer t)) (and internal (set-process-query-on-exit-flag process nil)))) @@ -1861,26 +1861,39 @@ When MSG is non-nil messages the first line of STRING." (string-match "\n[ \t].*\n?$" string)) (comint-send-string process "\n"))))) -;; Shell output catching stolen from gud-gdb -(defvar python-shell-fetch-lines-in-progress nil) -(defvar python-shell-fetch-lines-string nil) -(defvar python-shell-fetched-lines nil) - -(defun python-shell-fetch-lines-filter (string) - "Filter used to read the list of lines output by a command. -STRING is the output to filter." - (setq string (concat python-shell-fetch-lines-string string)) - (while (string-match "\n" string) - (push (substring string 0 (match-beginning 0)) - python-shell-fetched-lines) - (setq string (substring string (match-end 0)))) - (if (equal (string-match comint-prompt-regexp string) 0) - (progn - (setq python-shell-fetch-lines-in-progress nil) - string) - (progn - (setq python-shell-fetch-lines-string string) - ""))) +(defvar python-shell-output-filter-in-progress nil) +(defvar python-shell-output-filter-buffer nil) + +(defun python-shell-output-filter (string) + "Filter used in `python-shell-send-string-no-output' to grab output. +STRING is the output received to this point from the process. +This filter saves received output from the process in +`python-shell-output-filter-buffer' and stops receiving it after +detecting a prompt at the end of the buffer." + (setq + string (ansi-color-filter-apply string) + python-shell-output-filter-buffer + (concat python-shell-output-filter-buffer string)) + (when (string-match + (format "\n\\(?:%s\\|%s\\|%s\\)$" + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-pdb-regexp) + python-shell-output-filter-buffer) + ;; Output ends when `python-shell-output-filter-buffer' contains + ;; the prompt attached at the end of it. + (setq python-shell-output-filter-in-progress nil + python-shell-output-filter-buffer + (substring python-shell-output-filter-buffer + 0 (match-beginning 0))) + (when (and (> (length python-shell-prompt-output-regexp) 0) + (string-match (concat "^" python-shell-prompt-output-regexp) + python-shell-output-filter-buffer)) + ;; Some shells, like iPython might append a prompt before the + ;; output, clean that. + (setq python-shell-output-filter-buffer + (substring python-shell-output-filter-buffer (match-end 0))))) + "") (defun python-shell-send-string-no-output (string &optional process msg) "Send STRING to PROCESS and inhibit output. @@ -1888,18 +1901,20 @@ When MSG is non-nil messages the first line of STRING. Return the output." (let ((process (or process (python-shell-get-or-create-process))) (comint-preoutput-filter-functions - '(python-shell-fetch-lines-filter)) - (python-shell-fetch-lines-in-progress t) + '(python-shell-output-filter)) + (python-shell-output-filter-in-progress t) (inhibit-quit t)) (or (with-local-quit (python-shell-send-string string process msg) - (while python-shell-fetch-lines-in-progress + (while python-shell-output-filter-in-progress + ;; `python-shell-output-filter' takes care of setting + ;; `python-shell-output-filter-in-progress' to NIL after it + ;; detects end of output. (accept-process-output process)) (prog1 - (mapconcat #'identity - (reverse python-shell-fetched-lines) "\n") - (setq python-shell-fetched-lines nil))) + python-shell-output-filter-buffer + (setq python-shell-output-filter-buffer nil))) (with-current-buffer (process-buffer process) (comint-interrupt-subjob))))) -- cgit v1.2.1 From be0d5baecc14ae4dea87e159b803e7f8b1043451 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 7 Oct 2012 13:13:52 -0300 Subject: Enhancements to indentation. * lisp/progmodes/python.el (python-indent-context): Give priority to inside-string context. Make comments indentation markers. (python-indent-region): Do not mess with strings, unless it's the enclosing set of quotes. --- lisp/progmodes/python.el | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 726c0b2d542..3ac871981e4 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -676,12 +676,12 @@ START is the buffer position where the sexp starts." (goto-char (line-beginning-position)) (bobp)) 'no-indent) - ;; Inside a paren - ((setq start (python-syntax-context 'paren ppss)) - 'inside-paren) ;; Inside string ((setq start (python-syntax-context 'string ppss)) 'inside-string) + ;; Inside a paren + ((setq start (python-syntax-context 'paren ppss)) + 'inside-paren) ;; After backslash ((setq start (when (not (or (python-syntax-context 'string ppss) (python-syntax-context 'comment ppss))) @@ -710,7 +710,7 @@ START is the buffer position where the sexp starts." ;; After normal line ((setq start (save-excursion (back-to-indentation) - (python-util-forward-comment -1) + (skip-chars-backward (rx (or whitespace ?\n))) (python-nav-beginning-of-statement) (point-marker))) 'after-line) @@ -973,7 +973,16 @@ Called from a program, START and END specify the region to indent." (back-to-indentation) (setq word (current-word)) (forward-line 1) - (when word + (when (and word + ;; Don't mess with strings, unless it's the + ;; enclosing set of quotes. + (or (not (python-syntax-context 'string)) + (eq + (syntax-after + (+ (1- (point)) + (current-indentation) + (python-syntax-count-quotes (char-after) (point)))) + (string-to-syntax "|")))) (beginning-of-line) (delete-horizontal-space) (indent-to (python-indent-calculate-indentation))))) -- cgit v1.2.1 From f27c99dc7ef8f19e7a378001d8b3d906ca907dae Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sun, 7 Oct 2012 16:37:37 -0300 Subject: * progmodes/python.el (inferior-python-mode) (python-shell-make-comint): Fixed initialization of local variables copied from parent buffer. --- lisp/progmodes/python.el | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 3ac871981e4..ffb2e66ca9d 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1652,6 +1652,8 @@ uniqueness for different types of configurations." OUTPUT is a string with the contents of the buffer." (ansi-color-filter-apply output)) +(defvar python-shell--parent-buffer nil) + (define-derived-mode inferior-python-mode comint-mode "Inferior Python" "Major mode for Python inferior process. Runs a Python interpreter as a subprocess of Emacs, with Python @@ -1674,6 +1676,12 @@ initialization of the interpreter via `python-shell-setup-codes' variable. \(Type \\[describe-mode] in the process buffer for a list of commands.)" + (and python-shell--parent-buffer + (python-util-clone-local-variables python-shell--parent-buffer)) + (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" + python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-pdb-regexp)) (set-syntax-table python-mode-syntax-table) (setq mode-line-process '(":%s")) (make-local-variable 'comint-output-filter-functions) @@ -1721,15 +1729,10 @@ killed." (let* ((cmdlist (split-string-and-unquote cmd)) (buffer (apply #'make-comint-in-buffer proc-name proc-buffer-name (car cmdlist) nil (cdr cmdlist))) - (current-buffer (current-buffer)) + (python-shell--parent-buffer (current-buffer)) (process (get-buffer-process buffer))) (with-current-buffer buffer - (inferior-python-mode) - (python-util-clone-local-variables current-buffer) - (setq comint-prompt-regexp (format "^\\(?:%s\\|%s\\|%s\\)" - python-shell-prompt-regexp - python-shell-prompt-block-regexp - python-shell-prompt-pdb-regexp))) + (inferior-python-mode)) (accept-process-output process) (and pop (pop-to-buffer buffer t)) (and internal (set-process-query-on-exit-flag process nil)))) -- cgit v1.2.1 From 8dbce54cc73d16375b8d233da7271054eb4cda34 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 8 Oct 2012 02:19:15 -0300 Subject: Enhancements on forward-sexp movement. * progmodes/python.el (python-nav-beginning-of-statement) (python-nav-end-of-statement): Return point-marker. (python-nav-forward-sexp): lisp-like forward-sexp behavior. (python-info-current-symbol) (python-info-statement-starts-block-p): Rename from python-info-beginning-of-block-p. (python-info-statement-ends-block-p): Rename from python-info-end-of-block-p. (python-info-beginning-of-statement-p) (python-info-end-of-statement-p) (python-info-beginning-of-block-p, python-info-end-of-block-p): New functions. --- lisp/progmodes/python.el | 276 ++++++++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 145 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ffb2e66ca9d..6b0dc954ca7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1169,7 +1169,8 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p)) (python-syntax-context 'string) (python-syntax-context 'paren)) - (forward-line -1))))) + (forward-line -1)))) + (point-marker)) (defun python-nav-end-of-statement () "Move to end of current statement." @@ -1180,7 +1181,8 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p) (python-syntax-context 'string) (python-syntax-context 'paren)) - (forward-line 1))))) + (forward-line 1)))) + (point-marker)) (defun python-nav-backward-statement (&optional arg) "Move backward to previous statement. @@ -1295,151 +1297,104 @@ When ARG > 0 move forward, else if ARG is < 0." (while (and (funcall search-fn paren-regexp nil t) (python-syntax-context 'paren))))))) -(defun python-nav--forward-sexp () - "Move to forward sexp." - (case (python-syntax-context-type) - (string - ;; Inside of a string, get out of it. - (while (and (re-search-forward "[\"']" nil t) - (python-syntax-context 'string)))) - (comment - ;; Inside of a comment, just move forward. - (python-util-forward-comment)) - (paren - (python-nav-lisp-forward-sexp-safe 1)) - (t - (if (and (not (eobp)) - (= (syntax-class (syntax-after (point))) 4)) - ;; Looking an open-paren - (python-nav-lisp-forward-sexp-safe 1) - (let ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (next-block-starting-pos - (save-excursion (python-nav-forward-block)))) - (cond - ((not block-starting-pos) - ;; Not inside a block, move to closest one. - (and next-block-starting-pos - (goto-char next-block-starting-pos))) - ((= (point) block-starting-pos) - ;; Point is at beginning of block - (if (and next-block-starting-pos - (< next-block-starting-pos block-ending-pos)) - ;; Beginning of next block is closer than current's - ;; end, move to it. - (goto-char next-block-starting-pos) - (goto-char block-ending-pos))) - ((= block-ending-pos (point)) - ;; Point is at end of current block - (let ((parent-block-end-pos - (save-excursion - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block)))) - (if (and parent-block-end-pos - (or (not next-block-starting-pos) - (> next-block-starting-pos parent-block-end-pos))) - ;; If the parent block ends before next block - ;; starts move to it. - (goto-char parent-block-end-pos) - (and next-block-starting-pos - (goto-char next-block-starting-pos))))) - (t (python-nav-end-of-block)))))))) +(defun python-nav--forward-sexp (&optional dir) + "Move to forward sexp. +With positive Optional argument DIR direction move forward, else +backwards." + (setq dir (or dir 1)) + (unless (= dir 0) + (let* ((forward-p (if (> dir 0) + (and (setq dir 1) t) + (and (setq dir -1) nil))) + (re-search-fn (if forward-p + 're-search-forward + 're-search-backward)) + (context-type (python-syntax-context-type))) + (cond + ((eq context-type 'string) + ;; Inside of a string, get out of it. + (while (and (funcall re-search-fn "[\"']" nil t) + (python-syntax-context 'string)))) + ((eq context-type 'comment) + ;; Inside of a comment, just move forward. + (python-util-forward-comment dir)) + ((or (eq context-type 'paren) + (and forward-p (looking-at (python-rx open-paren))) + (and (not forward-p) + (eq (syntax-class (syntax-after (1- (point)))) + (car (string-to-syntax ")"))))) + ;; Inside a paren or looking at it, lisp knows what to do. + (python-nav-lisp-forward-sexp-safe dir)) + (t + ;; This part handles the lispy feel of + ;; `python-nav-forward-sexp'. Knowing everything about the + ;; current context and the context of the next sexp tries to + ;; follow the lisp sexp motion commands in a symmetric manner. + (let* ((context + (cond + ((python-info-beginning-of-block-p) 'block-start) + ((python-info-end-of-block-p) 'block-end) + ((python-info-beginning-of-statement-p) 'statement-start) + ((python-info-end-of-statement-p) 'statement-end))) + (next-sexp-pos + (save-excursion + (python-nav-lisp-forward-sexp-safe dir) + (point))) + (next-sexp-context + (save-excursion + (goto-char next-sexp-pos) + (cond + ((python-info-beginning-of-block-p) 'block-start) + ((python-info-end-of-block-p) 'block-end) + ((python-info-beginning-of-statement-p) 'statement-start) + ((python-info-end-of-statement-p) 'statement-end) + ((python-info-statement-starts-block-p) 'starts-block) + ((python-info-statement-ends-block-p) 'ends-block))))) + (if forward-p + (cond ((and (not (eobp)) + (python-info-current-line-empty-p)) + (python-util-forward-comment dir) + (python-nav--forward-sexp dir)) + ((eq context 'block-start) + (python-nav-end-of-block)) + ((eq context 'statement-start) + (python-nav-end-of-statement)) + ((and (memq context '(statement-end block-end)) + (eq next-sexp-context 'ends-block)) + (goto-char next-sexp-pos) + (python-nav-end-of-block)) + ((and (memq context '(statement-end block-end)) + (eq next-sexp-context 'starts-block)) + (goto-char next-sexp-pos) + (python-nav-end-of-block)) + ((memq context '(statement-end block-end)) + (goto-char next-sexp-pos) + (python-nav-end-of-statement)) + (t (goto-char next-sexp-pos))) + (cond ((and (not (bobp)) + (python-info-current-line-empty-p)) + (python-util-forward-comment dir) + (python-nav--forward-sexp dir)) + ((eq context 'block-end) + (python-nav-beginning-of-block)) + ((eq context 'statement-end) + (python-nav-beginning-of-statement)) + ((and (memq context '(statement-start block-start)) + (eq next-sexp-context 'starts-block)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-block)) + ((and (memq context '(statement-start block-start)) + (eq next-sexp-context 'ends-block)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-block)) + ((memq context '(statement-start block-start)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-statement)) + (t (goto-char next-sexp-pos)))))))))) (defun python-nav--backward-sexp () "Move to backward sexp." - (case (python-syntax-context-type) - (string - ;; Inside of a string, get out of it. - (while (and (re-search-backward "[\"']" nil t) - (python-syntax-context 'string)))) - (comment - ;; Inside of a comment, just move backward. - (python-util-forward-comment -1)) - (paren - ;; Handle parens like we are lisp. - (python-nav-lisp-forward-sexp-safe -1)) - (t - (let* ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (prev-block-ending-pos - (save-excursion (when (python-nav-backward-block) - (python-nav-end-of-block)))) - (prev-block-parent-ending-pos - (save-excursion - (when prev-block-ending-pos - (goto-char prev-block-ending-pos) - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block))))) - (if (and (not (bobp)) - (= (syntax-class (syntax-after (1- (point)))) 5)) - ;; Char before point is a paren closing char, handle it - ;; like we are lisp. - (python-nav-lisp-forward-sexp-safe -1) - (cond - ((not block-ending-pos) - ;; Not in and ending pos, move to end of previous block. - (and (python-nav-backward-block) - (python-nav-end-of-block))) - ((= (point) block-ending-pos) - ;; In ending pos, we need to search backwards for the - ;; closest point looking the list of candidates from here. - (let ((candidates)) - (dolist (name - '(prev-block-parent-ending-pos - prev-block-ending-pos - block-ending-pos - block-starting-pos)) - (when (and (symbol-value name) - (< (symbol-value name) (point))) - (add-to-list 'candidates (symbol-value name)))) - (goto-char (apply 'max candidates)))) - ((> (point) block-ending-pos) - ;; After an ending position, move to it. - (goto-char block-ending-pos)) - ((= (point) block-starting-pos) - ;; On a block starting position. - (if (not (> (point) (or prev-block-ending-pos (point)))) - ;; Point is after the end position of the block that - ;; wraps the current one, just move a block backward. - (python-nav-backward-block) - ;; If we got here we are facing a case like this one: - ;; - ;; try: - ;; return here() - ;; except Exception as e: - ;; - ;; Where point is on the "except" and must move to the - ;; end of "here()". - (goto-char prev-block-ending-pos) - (let ((parent-block-ending-pos - (save-excursion - (python-nav-forward-sexp) - (and (not (looking-at (python-rx block-start))) - (point))))) - (when (and parent-block-ending-pos - (> parent-block-ending-pos prev-block-ending-pos)) - ;; If we got here we are facing a case like this one: - ;; - ;; except ImportError: - ;; if predicate(): - ;; processing() - ;; here() - ;; except AttributeError: - ;; - ;; Where point is on the "except" and must move to - ;; the end of "here()". Without this extra step we'd - ;; just get to the end of processing(). - (goto-char parent-block-ending-pos))))) - (t - (if (and prev-block-ending-pos (< prev-block-ending-pos (point))) - (goto-char prev-block-ending-pos) - (python-nav-beginning-of-block))))))))) + (python-nav--forward-sexp -1)) (defun python-nav-forward-sexp (&optional arg) "Move forward across one block of code. @@ -2891,12 +2846,43 @@ parent defun name." ".") ".") name))))))) -(defsubst python-info-beginning-of-block-statement-p () +(defun python-info-statement-starts-block-p () "Return non-nil if current statement opens a block." (save-excursion (python-nav-beginning-of-statement) (looking-at (python-rx block-start)))) +(defun python-info-statement-ends-block-p () + "Return non-nil if point is at end of block." + (let ((end-of-block-pos (save-excursion + (python-nav-end-of-block))) + (end-of-statement-pos (save-excursion + (python-nav-end-of-statement)))) + (and end-of-block-pos end-of-statement-pos + (= end-of-block-pos end-of-statement-pos)))) + +(defun python-info-beginning-of-statement-p () + "Return non-nil if point is at beginning of statement." + (= (point) (save-excursion + (python-nav-beginning-of-statement) + (point)))) + +(defun python-info-end-of-statement-p () + "Return non-nil if point is at end of statement." + (= (point) (save-excursion + (python-nav-end-of-statement) + (point)))) + +(defun python-info-beginning-of-block-p () + "Return non-nil if point is at beginning of block." + (and (python-info-beginning-of-statement-p) + (python-info-statement-starts-block-p))) + +(defun python-info-end-of-block-p () + "Return non-nil if point is at end of block." + (and (python-info-end-of-statement-p) + (python-info-statement-ends-block-p))) + (defun python-info-closing-block () "Return the point of the block the current line closes." (let ((closing-word (save-excursion -- cgit v1.2.1 From a5b773c40ce950ac004be26b9f1bc4fcf985276b Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 8 Oct 2012 18:30:36 -0300 Subject: Fix shell handling of unbalanced quotes and parens in output. * progmodes/python.el (python-rx-constituents): Added string-delimiter. (python-syntax-propertize-function): Use it. (python-shell-output-syntax-table): New var. (inferior-python-mode): Prevent unbalanced parens/quotes from previous output mess with current input context. --- lisp/progmodes/python.el | 72 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 24 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 6b0dc954ca7..d35dbc69a48 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -337,19 +337,28 @@ "==" ">=" "is" "not"))) ;; FIXME: Use regexp-opt. (assignment-operator . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" - ">>=" "<<=" "&=" "^=" "|=")))) - "Additional Python specific sexps for `python-rx'")) - -(defmacro python-rx (&rest regexps) - "Python mode specialized rx macro. + ">>=" "<<=" "&=" "^=" "|="))) + (string-delimiter . ,(rx (and + ;; Match even number of backslashes. + (or (not (any ?\\ ?\' ?\")) point + ;; Quotes might be preceded by a escaped quote. + (and (or (not (any ?\\)) point) ?\\ + (* ?\\ ?\\) (any ?\' ?\"))) + (* ?\\ ?\\) + ;; Match single or triple quotes of any kind. + (group (or "\"" "\"\"\"" "'" "'''")))))) + "Additional Python specific sexps for `python-rx'") + + (defmacro python-rx (&rest regexps) + "Python mode specialized rx macro. This variant of `rx' supports common python named REGEXPS." - (let ((rx-constituents (append python-rx-constituents rx-constituents))) - (cond ((null regexps) - (error "No regexp")) - ((cdr regexps) - (rx-to-string `(and ,@regexps) t)) - (t - (rx-to-string (car regexps) t))))) + (let ((rx-constituents (append python-rx-constituents rx-constituents))) + (cond ((null regexps) + (error "No regexp")) + ((cdr regexps) + (rx-to-string `(and ,@regexps) t)) + (t + (rx-to-string (car regexps) t)))))) ;;; Font-lock and syntax @@ -498,16 +507,7 @@ The type returned can be `comment', `string' or `paren'." (defconst python-syntax-propertize-function (syntax-propertize-rules - ((rx - (and - ;; Match even number of backslashes. - (or (not (any ?\\ ?\' ?\")) point - ;; Quotes might be preceded by a escaped quote. - (and (or (not (any ?\\)) point) ?\\ - (* ?\\ ?\\) (any ?\' ?\"))) - (* ?\\ ?\\) - ;; Match single or triple quotes of any kind. - (group (or "\"" "\"\"\"" "'" "'''")))) + ((python-rx string-delimiter) (0 (ignore (python-syntax-stringify)))))) (defsubst python-syntax-count-quotes (quote-char &optional point limit) @@ -1609,6 +1609,20 @@ OUTPUT is a string with the contents of the buffer." (defvar python-shell--parent-buffer nil) +(defvar python-shell-output-syntax-table + (let ((table (make-syntax-table python-dotty-syntax-table))) + (modify-syntax-entry ?\' "." table) + (modify-syntax-entry ?\" "." table) + (modify-syntax-entry ?\( "." table) + (modify-syntax-entry ?\[ "." table) + (modify-syntax-entry ?\{ "." table) + (modify-syntax-entry ?\) "." table) + (modify-syntax-entry ?\] "." table) + (modify-syntax-entry ?\} "." table) + table) + "Syntax table for shell output. +It makes parens and quotes be treated as punctuation chars.") + (define-derived-mode inferior-python-mode comint-mode "Inferior Python" "Major mode for Python inferior process. Runs a Python interpreter as a subprocess of Emacs, with Python @@ -1637,7 +1651,6 @@ variable. python-shell-prompt-regexp python-shell-prompt-block-regexp python-shell-prompt-pdb-regexp)) - (set-syntax-table python-mode-syntax-table) (setq mode-line-process '(":%s")) (make-local-variable 'comint-output-filter-functions) (add-hook 'comint-output-filter-functions @@ -1658,10 +1671,21 @@ variable. (make-local-variable 'python-pdbtrack-tracked-buffer) (make-local-variable 'python-shell-internal-last-output) (when python-shell-enable-font-lock + (set-syntax-table python-mode-syntax-table) (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords nil nil nil nil)) (set (make-local-variable 'syntax-propertize-function) - python-syntax-propertize-function)) + (syntax-propertize-rules + (comint-prompt-regexp + (0 (ignore + (put-text-property + comint-last-input-start end 'syntax-table + python-shell-output-syntax-table) + (font-lock-unfontify-region comint-last-input-start end)))) + ((python-rx string-delimiter) + (0 (ignore + (and (not (eq (get-text-property start 'field) 'output)) + (python-syntax-stringify)))))))) (compilation-shell-minor-mode 1)) (defun python-shell-make-comint (cmd proc-name &optional pop internal) -- cgit v1.2.1 From 51867ae22506264c0893b5e1a9ec7967157b6d96 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 8 Oct 2012 18:51:02 -0300 Subject: * progmodes/python.el (python-shell-output-filter): Handle extra carriage return in OSX. Fixes: debbugs:12409 --- lisp/progmodes/python.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index d35dbc69a48..018d6a7a1f2 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1866,7 +1866,9 @@ detecting a prompt at the end of the buffer." python-shell-output-filter-buffer (concat python-shell-output-filter-buffer string)) (when (string-match - (format "\n\\(?:%s\\|%s\\|%s\\)$" + ;; XXX: It seems on OSX an extra carriage return is attached + ;; at the end of output, this handles that too. + (format "\r?\n\\(?:%s\\|%s\\|%s\\)$" python-shell-prompt-regexp python-shell-prompt-block-regexp python-shell-prompt-pdb-regexp) -- cgit v1.2.1 From 24517d82a98eaeb33f44854e72bfbf342bf24ea4 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 8 Oct 2012 19:18:05 -0300 Subject: * progmodes/python.el (python-fill-paragraph): Rename from python-fill-paragraph-function. Fixed fill-paragraph for decorators. Fixes: debbugs:12605 --- lisp/progmodes/python.el | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 018d6a7a1f2..0bde8ce3334 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2272,28 +2272,28 @@ inferior python process is updated properly." (defcustom python-fill-comment-function 'python-fill-comment "Function to fill comments. -This is the function used by `python-fill-paragraph-function' to +This is the function used by `python-fill-paragraph' to fill comments." :type 'symbol :group 'python) (defcustom python-fill-string-function 'python-fill-string "Function to fill strings. -This is the function used by `python-fill-paragraph-function' to +This is the function used by `python-fill-paragraph' to fill strings." :type 'symbol :group 'python) (defcustom python-fill-decorator-function 'python-fill-decorator "Function to fill decorators. -This is the function used by `python-fill-paragraph-function' to +This is the function used by `python-fill-paragraph' to fill decorators." :type 'symbol :group 'python) (defcustom python-fill-paren-function 'python-fill-paren "Function to fill parens. -This is the function used by `python-fill-paragraph-function' to +This is the function used by `python-fill-paragraph' to fill parens." :type 'symbol :group 'python) @@ -2370,7 +2370,7 @@ SYMMETRIC: :safe (lambda (val) (memq val '(django onetwo pep-257 pep-257-nn symmetric nil)))) -(defun python-fill-paragraph-function (&optional justify) +(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 @@ -2389,8 +2389,7 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (funcall python-fill-string-function justify)) ;; Decorators ((equal (char-after (save-excursion - (back-to-indentation) - (point))) ?@) + (python-nav-beginning-of-statement))) ?@) (funcall python-fill-decorator-function justify)) ;; Parens ((or (python-syntax-context 'paren) @@ -2402,12 +2401,12 @@ Optional argument JUSTIFY defines if the paragraph should be justified." (t t)))) (defun python-fill-comment (&optional justify) - "Comment fill function for `python-fill-paragraph-function'. + "Comment fill function for `python-fill-paragraph'. JUSTIFY should be used (if applicable) as in `fill-paragraph'." (fill-comment-paragraph justify)) (defun python-fill-string (&optional justify) - "String fill function for `python-fill-paragraph-function'. + "String fill function for `python-fill-paragraph'. JUSTIFY should be used (if applicable) as in `fill-paragraph'." (let* ((marker (point-marker)) (str-start-pos @@ -2477,12 +2476,12 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." (indent-according-to-mode))))) t) (defun python-fill-decorator (&optional justify) - "Decorator fill function for `python-fill-paragraph-function'. + "Decorator fill function for `python-fill-paragraph'. JUSTIFY should be used (if applicable) as in `fill-paragraph'." t) (defun python-fill-paren (&optional justify) - "Paren fill function for `python-fill-paragraph-function'. + "Paren fill function for `python-fill-paragraph'. JUSTIFY should be used (if applicable) as in `fill-paragraph'." (save-restriction (narrow-to-region (progn @@ -3133,7 +3132,7 @@ if that value is non-nil." (set (make-local-variable 'paragraph-start) "\\s-*$") (set (make-local-variable 'fill-paragraph-function) - 'python-fill-paragraph-function) + 'python-fill-paragraph) (set (make-local-variable 'beginning-of-defun-function) #'python-beginning-of-defun-function) -- cgit v1.2.1 From a4ff7fe1452e56d2c11ca31652bd145868e87e17 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 8 Oct 2012 23:07:26 -0300 Subject: Implemented `backward-up-list'-like navigation. * progmodes/python.el (python-nav-up-list) (python-nav-backward-up-list): New functions. (python-mode-map): Define substitute key for backward-up-list to python-nav-backward-up-list. --- lisp/progmodes/python.el | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 0bde8ce3334..5bf64c18f99 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -235,6 +235,9 @@ (substitute-key-definition 'forward-sentence 'python-nav-forward-block map global-map) + (substitute-key-definition 'backward-up-list + 'python-nav-backward-up-list + map global-map) (define-key map "\C-c\C-j" 'imenu) ;; Indent specific (define-key map "\177" 'python-indent-dedent-line-backspace) @@ -1409,6 +1412,67 @@ move backward N times." (python-nav--backward-sexp) (setq arg (1+ arg)))) +(defun python-nav--up-list (&optional dir) + "Internal implementation of `python-nav-up-list'. +DIR is always 1 or -1 and comes sanitized from +`python-nav-up-list' calls." + (let ((context (python-syntax-context-type)) + (forward-p (> dir 0))) + (cond + ((memq context '(string comment))) + ((eq context 'paren) + (let ((forward-sexp-function)) + (up-list dir))) + ((and forward-p (python-info-end-of-block-p)) + (let ((parent-end-pos + (save-excursion + (let ((indentation (and + (python-nav-beginning-of-block) + (current-indentation)))) + (while (and indentation + (> indentation 0) + (>= (current-indentation) indentation) + (python-nav-backward-block))) + (python-nav-end-of-block))))) + (and (> (or parent-end-pos (point)) (point)) + (goto-char parent-end-pos)))) + (forward-p (python-nav-end-of-block)) + ((and (not forward-p) + (> (current-indentation) 0) + (python-info-beginning-of-block-p)) + (let ((prev-block-pos + (save-excursion + (let ((indentation (current-indentation))) + (while (and (python-nav-backward-block) + (> (current-indentation) indentation)))) + (point)))) + (and (> (point) prev-block-pos) + (goto-char prev-block-pos)))) + ((not forward-p) (python-nav-beginning-of-block))))) + +(defun python-nav-up-list (&optional arg) + "Move forward out of one level of parentheses (or blocks). +With ARG, do this that many times. +A negative argument means move backward but still to a less deep spot. +This command assumes point is not in a string or comment." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (python-nav--up-list 1) + (setq arg (1- arg))) + (while (< arg 0) + (python-nav--up-list -1) + (setq arg (1+ arg)))) + +(defun python-nav-backward-up-list (&optional arg) + "Move backward out of one level of parentheses (or blocks). +With ARG, do this that many times. +A negative argument means move backward but still to a less deep spot. +This command assumes point is not in a string or comment." + (interactive "^p") + (or arg (setq arg 1)) + (python-nav-up-list (- arg))) + ;;; Shell integration -- cgit v1.2.1 From 55cd00c8f958ff627521c73113a5ea66fe4e4a57 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Thu, 11 Oct 2012 21:07:25 -0300 Subject: * progmodes/python.el (python-mode-map): Replace subtitute-key-definition with proper command remapping. (python-nav--up-list): Fix behavior for blocks on the same level. --- lisp/progmodes/python.el | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5bf64c18f99..ff805d64024 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -229,15 +229,9 @@ (defvar python-mode-map (let ((map (make-sparse-keymap))) ;; Movement - (substitute-key-definition 'backward-sentence - 'python-nav-backward-block - map global-map) - (substitute-key-definition 'forward-sentence - 'python-nav-forward-block - map global-map) - (substitute-key-definition 'backward-up-list - 'python-nav-backward-up-list - map global-map) + (define-key map [remap backward-sentence] 'python-nav-backward-block) + (define-key map [remap forward-sentence] 'python-nav-forward-block) + (define-key map [remap backward-up-list] 'python-nav-backward-up-list) (define-key map "\C-c\C-j" 'imenu) ;; Indent specific (define-key map "\177" 'python-indent-dedent-line-backspace) @@ -1444,7 +1438,7 @@ DIR is always 1 or -1 and comes sanitized from (save-excursion (let ((indentation (current-indentation))) (while (and (python-nav-backward-block) - (> (current-indentation) indentation)))) + (>= (current-indentation) indentation)))) (point)))) (and (> (point) prev-block-pos) (goto-char prev-block-pos)))) -- cgit v1.2.1 From 12fd5ee1ae617b212e4d00e2ed4c196d3656f615 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Sat, 3 Nov 2012 09:20:02 -0300 Subject: * progmodes/python.el (inferior-python-mode): Fix hang in jit-lock. Fixes: debbugs:12645 --- lisp/progmodes/python.el | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ff805d64024..ee98feaef5e 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1733,17 +1733,24 @@ variable. (set (make-local-variable 'font-lock-defaults) '(python-font-lock-keywords nil nil nil nil)) (set (make-local-variable 'syntax-propertize-function) - (syntax-propertize-rules - (comint-prompt-regexp - (0 (ignore - (put-text-property - comint-last-input-start end 'syntax-table - python-shell-output-syntax-table) - (font-lock-unfontify-region comint-last-input-start end)))) - ((python-rx string-delimiter) - (0 (ignore - (and (not (eq (get-text-property start 'field) 'output)) - (python-syntax-stringify)))))))) + (eval + ;; XXX: Unfortunately eval is needed here to make use of the + ;; dynamic value of `comint-prompt-regexp'. + `(syntax-propertize-rules + (,comint-prompt-regexp + (0 (ignore + (put-text-property + comint-last-input-start end 'syntax-table + python-shell-output-syntax-table) + ;; XXX: This might look weird, but it is the easiest + ;; way to ensure font lock gets cleaned up before the + ;; current prompt, which is needed for unclosed + ;; strings to not mess up with current input. + (font-lock-unfontify-region comint-last-input-start end)))) + (,(python-rx string-delimiter) + (0 (ignore + (and (not (eq (get-text-property start 'field) 'output)) + (python-syntax-stringify))))))))) (compilation-shell-minor-mode 1)) (defun python-shell-make-comint (cmd proc-name &optional pop internal) -- cgit v1.2.1 From 2e6625b527f5ad134e28af5c2eba0349c640c942 Mon Sep 17 00:00:00 2001 From: FabiĆ”n Ezequiel Gallina Date: Mon, 12 Nov 2012 10:26:50 -0300 Subject: Fix end-of-defun misbehavior. * progmodes/python.el (python-nav-beginning-of-defun): Rename from python-beginning-of-defun-function. Handle nested defuns correctly. (python-nav-end-of-defun): Rename from python-end-of-defun-function. Ensure forward movement. (python-info-current-defun): Reimplemented to work as intended with new fixed python-nav-{end,beginning}-of-defun. Stop scanning parent defuns as soon as possible. --- lisp/progmodes/python.el | 137 ++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 68 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ee98feaef5e..949b0252bf1 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1074,12 +1074,9 @@ automatically if needed." The name of the defun should be grouped so it can be retrieved via `match-string'.") -(defun python-nav-beginning-of-defun (&optional arg) - "Move point to `beginning-of-defun'. -With positive ARG move search backwards. With negative do the -same but forward. When ARG is nil or 0 defaults to 1. This is -the main part of `python-beginning-of-defun-function'. Return -non-nil if point is moved to `beginning-of-defun'." +(defun python-nav--beginning-of-defun (&optional arg) + "Internal implementation of `python-nav-beginning-of-defun'. +With positive ARG search backwards, else search forwards." (when (or (null arg) (= arg 0)) (setq arg 1)) (let* ((re-search-fn (if (> arg 0) #'re-search-backward @@ -1087,6 +1084,15 @@ non-nil if point is moved to `beginning-of-defun'." (line-beg-pos (line-beginning-position)) (line-content-start (+ line-beg-pos (current-indentation))) (pos (point-marker)) + (beg-indentation + (and (> arg 0) + (save-excursion + (and (python-info-current-line-empty-p) + (python-util-forward-comment -1)) + (python-nav-beginning-of-statement) + (if (python-info-looking-at-beginning-of-defun) + (+ (current-indentation) python-indent-offset) + (current-indentation))))) (found (progn (when (and (< arg 0) @@ -1094,7 +1100,12 @@ non-nil if point is moved to `beginning-of-defun'." (end-of-line 1)) (while (and (funcall re-search-fn python-nav-beginning-of-defun-regexp nil t) - (python-syntax-context-type))) + (or (python-syntax-context-type) + ;; Handle nested defuns when moving + ;; backwards by checking indentation. + (and (> arg 0) + (not (= (current-indentation) 0)) + (>= (current-indentation) beg-indentation))))) (and (python-info-looking-at-beginning-of-defun) (or (not (= (line-number-at-pos pos) (line-number-at-pos))) @@ -1105,55 +1116,43 @@ non-nil if point is moved to `beginning-of-defun'." (or (beginning-of-line 1) t) (and (goto-char pos) nil)))) -(defun python-beginning-of-defun-function (&optional arg) - "Move point to the beginning of def or class. -With positive ARG move that number of functions backwards. With -negative do the same but forward. When ARG is nil or 0 defaults -to 1. Return non-nil if point is moved to `beginning-of-defun'." +(defun python-nav-beginning-of-defun (&optional arg) + "Move point to `beginning-of-defun'. +With positive ARG search backwards else search forward. When ARG +is nil or 0 defaults to 1. When searching backwards nested +defuns are handled with care depending on current point +position. Return non-nil if point is moved to +`beginning-of-defun'." (when (or (null arg) (= arg 0)) (setq arg 1)) (let ((found)) (cond ((and (eq this-command 'mark-defun) (python-info-looking-at-beginning-of-defun))) (t (dotimes (i (if (> arg 0) arg (- arg))) - (when (and (python-nav-beginning-of-defun arg) + (when (and (python-nav--beginning-of-defun arg) (not found)) (setq found t))))) found)) -(defun python-end-of-defun-function () +(defun python-nav-end-of-defun () "Move point to the end of def or class. Returns nil if point is not in a def or class." (interactive) - (let ((beg-defun-indent)) + (let ((beg-defun-indent) + (beg-pos (point))) (when (or (python-info-looking-at-beginning-of-defun) - (python-beginning-of-defun-function 1) - (python-beginning-of-defun-function -1)) + (python-nav-beginning-of-defun 1) + (python-nav-beginning-of-defun -1)) (setq beg-defun-indent (current-indentation)) + (while (progn + (python-nav-end-of-statement) + (python-util-forward-comment 1) + (and (> (current-indentation) beg-defun-indent) + (not (eobp))))) + (python-util-forward-comment -1) (forward-line 1) - ;; Go as forward as possible - (while (and (or - (python-nav-beginning-of-defun -1) - (and (goto-char (point-max)) nil)) - (> (current-indentation) beg-defun-indent))) - (beginning-of-line 1) - ;; Go as backwards as possible - (while (and (forward-line -1) - (not (bobp)) - (or (not (current-word)) - (equal (char-after (+ (point) (current-indentation))) ?#) - (<= (current-indentation) beg-defun-indent) - (looking-at (python-rx decorator)) - (python-syntax-context-type)))) - (forward-line 1) - ;; If point falls inside a paren or string context the point is - ;; forwarded at the end of it (or end of buffer if its not closed) - (let ((context-type (python-syntax-context-type))) - (when (memq context-type '(paren string)) - ;; Slow but safe. - (while (and (not (eobp)) - (python-syntax-context-type)) - (forward-line 1))))))) + ;; Ensure point moves forward. + (and (> beg-pos (point)) (goto-char beg-pos))))) (defun python-nav-beginning-of-statement () "Move to start of current statement." @@ -2022,7 +2021,7 @@ When argument ARG is non-nil do not include decorators." (python-shell-send-region (progn (end-of-line 1) - (while (and (or (python-beginning-of-defun-function) + (while (and (or (python-nav-beginning-of-defun) (beginning-of-line 1)) (> (current-indentation) 0))) (when (not arg) @@ -2031,7 +2030,7 @@ When argument ARG is non-nil do not include decorators." (forward-line 1)) (point-marker)) (progn - (or (python-end-of-defun-function) + (or (python-nav-end-of-defun) (end-of-line 1)) (point-marker))))) @@ -2879,38 +2878,40 @@ Optional argument INCLUDE-TYPE indicates to include the type of the defun. This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." - (let ((names '()) - (starting-indentation) - (starting-point) - (first-run t)) (save-restriction (widen) (save-excursion - (setq starting-point (point-marker)) - (setq starting-indentation (save-excursion - (python-nav-beginning-of-statement) - (current-indentation))) (end-of-line 1) - (while (python-beginning-of-defun-function 1) - (when (or (< (current-indentation) starting-indentation) - (and first-run - (< - starting-point - (save-excursion - (python-end-of-defun-function) - (point-marker))))) - (setq first-run nil) - (setq starting-indentation (current-indentation)) - (looking-at python-nav-beginning-of-defun-regexp) - (setq names (cons + (let ((names) + (starting-indentation + (save-excursion + (and + (python-nav-beginning-of-defun 1) + ;; This extra number is just for checking code + ;; against indentation to work well on first run. + (+ (current-indentation) 4)))) + (starting-point (point))) + ;; Check point is inside a defun. + (when (and starting-indentation + (< starting-point + (save-excursion + (python-nav-end-of-defun) + (point)))) + (catch 'exit + (while (python-nav-beginning-of-defun 1) + (when (< (current-indentation) starting-indentation) + (setq starting-indentation (current-indentation)) + (setq names + (cons (if (not include-type) (match-string-no-properties 1) (mapconcat 'identity (split-string (match-string-no-properties 0)) " ")) - names)))))) - (when names - (mapconcat (lambda (string) string) names ".")))) + names))) + (and (= (current-indentation) 0) (throw 'exit t))))) + (and names + (mapconcat (lambda (string) string) names ".")))))) (defun python-info-current-symbol (&optional replace-self) "Return current symbol using dotty syntax. @@ -3200,9 +3201,9 @@ if that value is non-nil." 'python-fill-paragraph) (set (make-local-variable 'beginning-of-defun-function) - #'python-beginning-of-defun-function) + #'python-nav-beginning-of-defun) (set (make-local-variable 'end-of-defun-function) - #'python-end-of-defun-function) + #'python-nav-end-of-defun) (add-hook 'completion-at-point-functions 'python-completion-complete-at-point nil 'local) @@ -3230,7 +3231,7 @@ if that value is non-nil." (add-to-list 'hs-special-modes-alist `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" ,(lambda (arg) - (python-end-of-defun-function)) nil)) + (python-nav-end-of-defun)) nil)) (set (make-local-variable 'mode-require-final-newline) t) -- cgit v1.2.1 From 141462223d6f8063bf01692c2f41ecc58baea506 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 19 Nov 2012 16:30:55 -0500 Subject: * lisp/emacs-lisp/byte-run.el (defun-declarations-alist): Allow compiler-macros to be lambda expressions. * lisp/progmodes/python.el: Use cl-lib. Move var declarations outside of eval-when-compile. (python-syntax-context): Add compiler-macro. (python-font-lock-keywords): Simplify with De Morgan. --- lisp/progmodes/python.el | 77 +++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 33 deletions(-) (limited to 'lisp/progmodes/python.el') diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 949b0252bf1..550c5f5a129 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -202,13 +202,12 @@ (require 'ansi-color) (require 'comint) +(eval-when-compile (require 'cl-lib)) -(eval-when-compile - (require 'cl) - ;; Avoid compiler warnings - (defvar view-return-to-alist) - (defvar compilation-error-regexp-alist) - (defvar outline-heading-end-regexp)) +;; Avoid compiler warnings +(defvar view-return-to-alist) +(defvar compilation-error-regexp-alist) +(defvar outline-heading-end-regexp) (autoload 'comint-mode "comint") @@ -364,12 +363,24 @@ This variant of `rx' supports common python named REGEXPS." "Return non-nil if point is on TYPE using SYNTAX-PPSS. TYPE can be `comment', `string' or `paren'. It returns the start character address of the specified TYPE." + (declare (compiler-macro + (lambda (form) + (pcase type + (`'comment + `(let ((ppss (or ,syntax-ppss (syntax-ppss)))) + (and (nth 4 ppss) (nth 8 ppss)))) + (`'string + `(let ((ppss (or ,syntax-ppss (syntax-ppss)))) + (and (nth 3 ppss) (nth 8 ppss)))) + (`'paren + `(nth 1 (or ,syntax-ppss (syntax-ppss)))) + (_ form))))) (let ((ppss (or syntax-ppss (syntax-ppss)))) - (case type - (comment (and (nth 4 ppss) (nth 8 ppss))) - (string (and (not (nth 4 ppss)) (nth 8 ppss))) - (paren (nth 1 ppss)) - (t nil)))) + (pcase type + (`comment (and (nth 4 ppss) (nth 8 ppss))) + (`string (and (nth 3 ppss) (nth 8 ppss))) + (`paren (nth 1 ppss)) + (_ nil)))) (defun python-syntax-context-type (&optional syntax-ppss) "Return the context type using SYNTAX-PPSS. @@ -481,8 +492,8 @@ The type returned can be `comment', `string' or `paren'." (when (re-search-forward re limit t) (while (and (python-syntax-context 'paren) (re-search-forward re limit t))) - (if (and (not (python-syntax-context 'paren)) - (not (equal (char-after (point-marker)) ?=))) + (if (not (or (python-syntax-context 'paren) + (equal (char-after (point-marker)) ?=))) t (set-match-data nil))))) (1 font-lock-variable-name-face nil nil)) @@ -516,7 +527,7 @@ is used to limit the scan." (while (and (< i 3) (or (not limit) (< (+ point i) limit)) (eq (char-after (+ point i)) quote-char)) - (incf i)) + (cl-incf i)) i)) (defun python-syntax-stringify () @@ -723,17 +734,17 @@ START is the buffer position where the sexp starts." (save-restriction (widen) (save-excursion - (case context-status - ('no-indent 0) + (pcase context-status + (`no-indent 0) ;; When point is after beginning of block just add one level ;; of indentation relative to the context-start - ('after-beginning-of-block + (`after-beginning-of-block (goto-char context-start) (+ (current-indentation) python-indent-offset)) ;; When after a simple line just use previous line ;; indentation, in the case current line starts with a ;; `python-indent-dedenters' de-indent one level. - ('after-line + (`after-line (- (save-excursion (goto-char context-start) @@ -746,11 +757,11 @@ START is the buffer position where the sexp starts." ;; When inside of a string, do nothing. just use the current ;; indentation. XXX: perhaps it would be a good idea to ;; invoke standard text indentation here - ('inside-string + (`inside-string (goto-char context-start) (current-indentation)) ;; After backslash we have several possibilities. - ('after-backslash + (`after-backslash (cond ;; Check if current line is a dot continuation. For this ;; the current line must start with a dot and previous @@ -816,7 +827,7 @@ START is the buffer position where the sexp starts." (+ (current-indentation) python-indent-offset))))) ;; When inside a paren there's a need to handle nesting ;; correctly - ('inside-paren + (`inside-paren (cond ;; If current line closes the outermost open paren use the ;; current indentation of the context-start line. @@ -2164,11 +2175,11 @@ INPUT." 'default) (t nil))) (completion-code - (case completion-context - (pdb python-shell-completion-pdb-string-code) - (import python-shell-completion-module-string-code) - (default python-shell-completion-string-code) - (t nil))) + (pcase completion-context + (`pdb python-shell-completion-pdb-string-code) + (`import python-shell-completion-module-string-code) + (`default python-shell-completion-string-code) + (_ nil))) (input (if (eq completion-context 'import) (replace-regexp-in-string "^[ \t]+" "" line) @@ -2492,17 +2503,17 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'." ;; Docstring styles may vary for oneliners and multi-liners. (> (count-matches "\n" str-start-pos str-end-pos) 0)) (delimiters-style - (case python-fill-docstring-style + (pcase python-fill-docstring-style ;; delimiters-style is a cons cell with the form ;; (START-NEWLINES . END-NEWLINES). When any of the sexps ;; is NIL means to not add any newlines for start or end ;; of docstring. See `python-fill-docstring-style' for a ;; graphic idea of each style. - (django (cons 1 1)) - (onetwo (and multi-line-p (cons 1 2))) - (pep-257 (and multi-line-p (cons nil 2))) - (pep-257-nn (and multi-line-p (cons nil 1))) - (symmetric (and multi-line-p (cons 1 1))))) + (`django (cons 1 1)) + (`onetwo (and multi-line-p (cons 1 2))) + (`pep-257 (and multi-line-p (cons nil 2))) + (`pep-257-nn (and multi-line-p (cons nil 1))) + (`symmetric (and multi-line-p (cons 1 1))))) (docstring-p (save-excursion ;; Consider docstrings those strings which ;; start on a line by themselves. @@ -2703,7 +2714,7 @@ The skeleton will be bound to python-skeleton-NAME." (easy-menu-add-item nil '("Python" "Skeletons") `[,(format - "Insert %s" (caddr (split-string (symbol-name skeleton) "-"))) + "Insert %s" (nth 2 (split-string (symbol-name skeleton) "-"))) ,skeleton t])))) ;;; FFAP -- cgit v1.2.1