diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog index a9c4995d49b..a1d2688470d 100644 --- a/doc/emacs/ChangeLog +++ b/doc/emacs/ChangeLog @@ -1,3 +1,7 @@ +2014-07-21 Glenn Morris + + * emacs.texi (Intro): Workaround makeinfo 4 @acronym bug. (Bug#18040) + 2014-07-09 Juri Linkov * search.texi (Regexp Search): Update lax space matching that is diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index 51e226dd388..b1cb196c3c2 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1476,7 +1476,16 @@ Neal Ziring, Teodor Zlatanov, and Detlev Zundel. You are reading about GNU Emacs, the GNU incarnation of the advanced, self-documenting, customizable, extensible editor Emacs. -(The @samp{G} in @acronym{GNU, @acronym{GNU}'s Not Unix} is not silent.) +(The @samp{G} in +@c Workaround makeinfo 4 bug. +@c http://lists.gnu.org/archive/html/bug-texinfo/2004-08/msg00009.html +@iftex +@acronym{GNU, @acronym{GNU}'s Not Unix} +@end iftex +@ifnottex +@acronym{GNU, GNU's Not Unix} +@end ifnottex +is not silent.) We call Emacs @dfn{advanced} because it can do much more than simple insertion and deletion of text. It can control subprocesses, indent diff --git a/etc/NEWS b/etc/NEWS index 1a86f41314e..558e1608dfb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -870,6 +870,22 @@ display a "Homepage" header.) ** In Prolog mode, `prolog-use-smie' has been removed, along with the non-SMIE indentation code. +** Python mode + +*** Out of the box support for CPython, iPython and readline based shells. +**** `python-shell-completion-module-string-code` is no longer used. + +*** Automatic shell prompt detection. New user options: +**** `python-shell-interpreter-interactive-arg'. +**** `python-shell-prompt-detect-enabled'. +**** `python-shell-prompt-detect-failure-warning'. +**** `python-shell-prompt-input-regexps'. +**** `python-shell-prompt-output-regexps'. + +*** Python shell support for remote hosts via tramp. + +*** Correct display of line numbers for code sent to the Python shell. + ** Remember *** The new command `remember-notes' creates a buffer that is saved on exit. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 5ed7d6918d3..1c557c91602 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,67 @@ +2014-07-21 Glenn Morris + + * progmodes/hideif.el (hide-ifdef-mode-submap): + Also substitute read-only-mode. + * bindings.el (mode-line-toggle-read-only): + * bs.el (bs-toggle-readonly): + * buff-menu.el (Buffer-menu-toggle-read-only): + * dired.el (dired-toggle-read-only): + * files.el (view-read-only, find-file-read-only) + (find-file-read-only-other-window) + (find-file-read-only-other-frame): + * progmodes/hideif.el (hide-ifdef-toggle-outside-read-only): + Doc fixes re toggle-read-only. + +2014-07-21 Fabián Ezequiel Gallina + + * progmodes/python.el: Add comment about pipe buffering and + solutions for missing/delayed output in inferior Python shells. + (Bug#17304) + + * progmodes/python.el (python-mode): Don't set + mode-require-final-newline. (Bug#17990) + + Make python.el work with IPython automatically. (Bug#15510) + * progmodes/python.el: + (python-shell-completion-setup-code): New value supporting iPython. + (python-shell-completion-string-code): New value supporting iPython. + (python-shell-completion-get-completions): Use them. + (python-shell-completion-module-string-code): Make obsolete. + (python-shell-prompt-input-regexps) + (python-shell-prompt-output-regexps): Add safeguard for ipdb. + (python-shell-output-filter): Fix comment typo. + + Fix Python shell prompts detection for remote hosts. + * progmodes/python.el (python-shell-prompt-detect): Replace + call-process with process-file and make it more robust. + + Autodetect Python shell prompts. (Bug#17370) + * progmodes/python.el: + (python-shell-interpreter-interactive-arg) + (python-shell-prompt-detect-enabled) + (python-shell-prompt-detect-failure-warning) + (python-shell-prompt-input-regexps) + (python-shell-prompt-output-regexps): New vars. + (python-shell-prompt-calculated-input-regexp) + (python-shell-prompt-calculated-output-regexp): New vars. + (python-shell-get-process-name) + (python-shell-internal-get-process-name) + (python-shell-output-filter) + (python-shell-completion-get-completions): Use them. + (python-shell-prompt-detect) + (python-shell-prompt-validate-regexps): New functions. + (python-shell-prompt-set-calculated-regexps): New function. + (inferior-python-mode): Use it. Also honor overriden + python-shell-interpreter and python-shell-interpreter-args. + (python-shell-make-comint): Honor overriden + python-shell-interpreter and python-shell-interpreter-args. + (python-shell-get-or-create-process): Make it testable by allowing + to call run-python non-interactively. + (python-util-valid-regexp-p): New function. + (python-shell-prompt-regexp, python-shell-prompt-block-regexp) + (python-shell-prompt-output-regexp) + (python-shell-prompt-pdb-regexp): Use it as defcustom :safe. + 2014-07-21 Stefan Monnier * emacs-lisp/smie.el (smie-config--guess-1): Split from diff --git a/lisp/bindings.el b/lisp/bindings.el index 911ab63e8d0..ac31723e02d 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -36,7 +36,7 @@ corresponding to the mode line clicked." (defun mode-line-toggle-read-only (event) - "Like `toggle-read-only', for the mode-line." + "Like toggling `read-only-mode', for the mode-line." (interactive "e") (with-selected-window (posn-window (event-start event)) (read-only-mode 'toggle))) diff --git a/lisp/bs.el b/lisp/bs.el index 73b3684e566..5e9c6908f28 100644 --- a/lisp/bs.el +++ b/lisp/bs.el @@ -954,7 +954,7 @@ Default is `bs--current-sort-function'." (defun bs-toggle-readonly () "Toggle read-only status for buffer on current line. -Uses function `toggle-read-only'." +Uses function `read-only-mode'." (interactive) (with-current-buffer (bs--current-buffer) (read-only-mode 'toggle)) diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el index d85caca6216..f1772b3d20c 100644 --- a/lisp/buff-menu.el +++ b/lisp/buff-menu.el @@ -539,7 +539,7 @@ The current window remains selected." (defun Buffer-menu-toggle-read-only () "Toggle read-only status of buffer on this line. -This behaves like invoking \\[toggle-read-only] in that buffer." +This behaves like invoking \\[read-only-mode] in that buffer." (interactive) (let ((read-only (with-current-buffer (Buffer-menu-buffer t) diff --git a/lisp/dired.el b/lisp/dired.el index 25b70219c7d..4cc252c7492 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2015,7 +2015,7 @@ Actual changes in files cannot be undone by Emacs.")) "Edit Dired buffer with Wdired, or make it read-only. If the current buffer can be edited with Wdired, (i.e. the major mode is `dired-mode'), call `wdired-change-to-wdired-mode'. -Otherwise, call `toggle-read-only'." +Otherwise, toggle `read-only-mode'." (interactive) (if (derived-mode-p 'dired-mode) (wdired-change-to-wdired-mode) diff --git a/lisp/files.el b/lisp/files.el index d3aa063e788..252ae76fa0b 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -564,7 +564,7 @@ A value of nil means ignore them; anything else means query." In fact, this means that all read-only buffers normally have View mode enabled, including buffers that are read-only because you visit a file you cannot alter, and buffers you make read-only -using \\[toggle-read-only]." +using \\[read-only-mode]." :type 'boolean :group 'view) @@ -1502,7 +1502,7 @@ file names with wildcards." (defun find-file-read-only (filename &optional wildcards) "Edit file FILENAME but don't allow changes. Like \\[find-file], but marks buffer as read-only. -Use \\[toggle-read-only] to permit editing." +Use \\[read-only-mode] to permit editing." (interactive (find-file-read-args "Find file read-only: " (confirm-nonexistent-file-or-buffer))) @@ -1511,7 +1511,7 @@ Use \\[toggle-read-only] to permit editing." (defun find-file-read-only-other-window (filename &optional wildcards) "Edit file FILENAME in another window but don't allow changes. Like \\[find-file-other-window], but marks buffer as read-only. -Use \\[toggle-read-only] to permit editing." +Use \\[read-only-mode] to permit editing." (interactive (find-file-read-args "Find file read-only other window: " (confirm-nonexistent-file-or-buffer))) @@ -1520,7 +1520,7 @@ Use \\[toggle-read-only] to permit editing." (defun find-file-read-only-other-frame (filename &optional wildcards) "Edit file FILENAME in another frame but don't allow changes. Like \\[find-file-other-frame], but marks buffer as read-only. -Use \\[toggle-read-only] to permit editing." +Use \\[read-only-mode] to permit editing." (interactive (find-file-read-args "Find file read-only other frame: " (confirm-nonexistent-file-or-buffer))) diff --git a/lisp/progmodes/hideif.el b/lisp/progmodes/hideif.el index d60c009e66d..b1eaa6ad66c 100644 --- a/lisp/progmodes/hideif.el +++ b/lisp/progmodes/hideif.el @@ -188,6 +188,9 @@ Effective only if `hide-ifdef-expand-reinclusion-protection' is t." (define-key map "\C-q" 'hide-ifdef-toggle-read-only) (define-key map "\C-w" 'hide-ifdef-toggle-shadowing) + (substitute-key-definition + 'read-only-mode 'hide-ifdef-toggle-outside-read-only map) + ;; `toggle-read-only' is obsoleted by `read-only-mode'. (substitute-key-definition 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map) map) @@ -1789,7 +1792,7 @@ It does not do the work that's pointless to redo on a recursive entry." (force-mode-line-update)) (defun hide-ifdef-toggle-outside-read-only () - "Replacement for `toggle-read-only' within Hide-Ifdef mode." + "Replacement for `read-only-mode' within Hide-Ifdef mode." (interactive) (setq hif-outside-read-only (not hif-outside-read-only)) (message "Read only %s" diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1187636c663..89ef12d49eb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -4,7 +4,7 @@ ;; Author: Fabián E. Gallina ;; URL: https://github.com/fgallina/python.el -;; Version: 0.24.2 +;; Version: 0.24.4 ;; Maintainer: emacs-devel@gnu.org ;; Created: Jul 2010 ;; Keywords: languages @@ -62,57 +62,80 @@ ;; (add-hook 'python-mode-hook ;; (lambda () (setq forward-sexp-function nil))) -;; Shell interaction: is provided and allows you to execute easily any -;; block of code of your current buffer in an inferior Python process. +;; Shell interaction: is provided and allows opening Python shells +;; inside Emacs and executing any block of code of your current buffer +;; in that inferior Python process. + +;; Besides that only the standard CPython (2.x and 3.x) shell and +;; IPython are officially supported out of the box, the interaction +;; should support any other readline based Python shells as well +;; (e.g. Jython and Pypy have been reported to work). You can change +;; your default interpreter and commandline arguments by setting the +;; `python-shell-interpreter' and `python-shell-interpreter-args' +;; variables. This example enables IPython globally: + +;; (setq python-shell-interpreter "ipython" +;; python-shell-interpreter-args "-i") + +;; Using the "console" subcommand to start IPython in server-client +;; mode is known to fail intermittently due a bug on IPython itself +;; (see URL `http://debbugs.gnu.org/cgi/bugreport.cgi?bug=18052#27'). +;; There seems to be a race condition in the IPython server (A.K.A +;; kernel) when code is sent while it is still initializing, sometimes +;; causing the shell to get stalled. With that said, if an IPython +;; kernel is already running, "console --existing" seems to work fine. + +;; Running IPython on Windows needs more tweaking. The way you should +;; set `python-shell-interpreter' and `python-shell-interpreter-args' +;; is as follows (of course you need to modify the paths according to +;; your system): + +;; (setq python-shell-interpreter "C:\\Python27\\python.exe" +;; python-shell-interpreter-args +;; "-i C:\\Python27\\Scripts\\ipython-script.py") + +;; If you are experiencing missing or delayed output in your shells, +;; that's likely caused by your Operating System's pipe buffering +;; (e.g. this is known to happen running CPython 3.3.4 in Windows 7. +;; See URL `http://debbugs.gnu.org/cgi/bugreport.cgi?bug=17304'). To +;; fix this, using CPython's "-u" commandline argument or setting the +;; "PYTHONUNBUFFERED" environment variable should help: See URL +;; `https://docs.python.org/3/using/cmdline.html#cmdoption-u'. + +;; The interaction relies upon having prompts for input (e.g. ">>> " +;; and "... " in standard Python shell) and output (e.g. "Out[1]: " in +;; IPython) detected properly. Failing that Emacs may hang but, in +;; the case that happens, you can recover with \\[keyboard-quit]. To +;; avoid this issue, a two-step prompt autodetection mechanism is +;; provided: the first step is manual and consists of a collection of +;; regular expressions matching common prompts for Python shells +;; stored in `python-shell-prompt-input-regexps' and +;; `python-shell-prompt-output-regexps', and dir-local friendly vars +;; `python-shell-prompt-regexp', `python-shell-prompt-block-regexp', +;; `python-shell-prompt-output-regexp' which are appended to the +;; former automatically when a shell spawns; the second step is +;; automatic and depends on the `python-shell-prompt-detect' helper +;; function. See its docstring for details on global variables that +;; modify its behavior. ;; 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 specify your custom -;; `python-shell-completion-setup-code' and -;; `python-shell-completion-string-code'. - -;; Here is a complete example of the settings you would use for -;; iPython 0.11: - -;; (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 -;; "from IPython.core.completerlib import module_completion" -;; python-shell-completion-module-string-code -;; "';'.join(module_completion('''%s'''))\n" -;; python-shell-completion-string-code -;; "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") - -;; For iPython 0.10 everything would be the same except for -;; `python-shell-completion-string-code' and -;; `python-shell-completion-module-string-code': - -;; (setq python-shell-completion-string-code -;; "';'.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 -;; `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 +;; word. Shell completion is implemented in such way that if you +;; change the `python-shell-interpreter' it should be possible to +;; integrate custom logic to calculate completions. To achieve this +;; you just need to set `python-shell-completion-setup-code' and +;; `python-shell-completion-string-code'. The default provided code, +;; enables autocompletion for both CPython and IPython (and ideally +;; any readline based Python shell). This code 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. +;; bundles Python without it (like Windows), installing pyreadline +;; from URL `http://ipython.scipy.org/moin/PyReadline/Intro' should +;; suffice. To troubleshoot why you are not getting any completions +;; you can try the following in your Python shell: + +;; >>> import readline, rlcompleter + +;; If you see an error, then you need to either install pyreadline or +;; setup custom code that avoids that dependency. ;; Shell virtualenv support: The shell also contains support for ;; virtualenvs and other special environment modifications thanks to @@ -211,7 +234,9 @@ ;;; Code: (require 'ansi-color) +(require 'cl-lib) (require 'comint) +(require 'json) ;; Avoid compiler warnings (defvar view-return-to-alist) @@ -1705,33 +1730,60 @@ position, else returns nil." :type 'string :group 'python) -(defcustom python-shell-prompt-regexp ">>> " - "Regular expression matching top-level input prompt of Python shell. -It should not contain a caret (^) at the beginning." +(defcustom python-shell-interpreter-interactive-arg "-i" + "Interpreter argument to force it to run interactively." :type 'string - :group 'python - :safe 'stringp) + :version "24.4") -(defcustom python-shell-prompt-block-regexp "[.][.][.] " +(defcustom python-shell-prompt-detect-enabled t + "Non-nil enables autodetection of interpreter prompts." + :type 'boolean + :safe 'booleanp + :version "24.4") + +(defcustom python-shell-prompt-detect-failure-warning t + "Non-nil enables warnings when detection of prompts fail." + :type 'boolean + :safe 'booleanp + :version "24.4") + +(defcustom python-shell-prompt-input-regexps + '(">>> " "\\.\\.\\. " ; Python + "In \\[[0-9]+\\]: " ; IPython + ;; Using ipdb outside IPython may fail to cleanup and leave static + ;; IPython prompts activated, this adds some safeguard for that. + "In : " "\\.\\.\\.: ") + "List of regular expressions matching input prompts." + :type '(repeat string) + :version "24.4") + +(defcustom python-shell-prompt-output-regexps + '("" ; Python + "Out\\[[0-9]+\\]: " ; IPython + "Out :") ; ipdb safeguard + "List of regular expressions matching output prompts." + :type '(repeat string) + :version "24.4") + +(defcustom python-shell-prompt-regexp ">>> " + "Regular expression matching top level input prompt of Python shell. +It should not contain a caret (^) at the beginning." + :type 'string) + +(defcustom python-shell-prompt-block-regexp "\\.\\.\\. " "Regular expression matching block input prompt of Python shell. It should not contain a caret (^) at the beginning." - :type 'string - :group 'python - :safe 'stringp) + :type 'string) (defcustom python-shell-prompt-output-regexp "" "Regular expression matching output prompt of Python shell. It should not contain a caret (^) at the beginning." - :type 'string - :group 'python - :safe 'stringp) + :type 'string) (defcustom python-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " "Regular expression matching pdb input prompt of Python shell. It should not contain a caret (^) at the beginning." - :type 'string - :group 'python - :safe 'stringp) + :type 'string) (defcustom python-shell-enable-font-lock t "Should syntax highlighting be enabled in the Python shell buffer? @@ -1801,6 +1853,167 @@ virtualenv." :type '(alist string) :group 'python) +(defvar python-shell--prompt-calculated-input-regexp nil + "Calculated input prompt regexp for inferior python shell. +Do not set this variable directly, instead use +`python-shell-prompt-set-calculated-regexps'.") + +(defvar python-shell--prompt-calculated-output-regexp nil + "Calculated output prompt regexp for inferior python shell. +Do not set this variable directly, instead use +`python-shell-set-prompt-regexp'.") + +(defun python-shell-prompt-detect () + "Detect prompts for the current `python-shell-interpreter'. +When prompts can be retrieved successfully from the +`python-shell-interpreter' run with +`python-shell-interpreter-interactive-arg', returns a list of +three elements, where the first two are input prompts and the +last one is an output prompt. When no prompts can be detected +and `python-shell-prompt-detect-failure-warning' is non-nil, +shows a warning with instructions to avoid hangs and returns nil. +When `python-shell-prompt-detect-enabled' is nil avoids any +detection and just returns nil." + (when python-shell-prompt-detect-enabled + (let* ((process-environment (python-shell-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path)) + (code (concat + "import sys\n" + "ps = [getattr(sys, 'ps%s' % i, '') for i in range(1,4)]\n" + ;; JSON is built manually for compatibility + "ps_json = '\\n[\"%s\", \"%s\", \"%s\"]\\n' % tuple(ps)\n" + "print (ps_json)\n" + "sys.exit(0)\n")) + (output + (with-temp-buffer + ;; TODO: improve error handling by using + ;; `condition-case' and displaying the error message to + ;; the user in the no-prompts warning. + (ignore-errors + (let ((code-file (python-shell--save-temp-file code))) + ;; Use `process-file' as it is remote-host friendly. + (process-file + (executable-find python-shell-interpreter) + code-file + '(t nil) + nil + python-shell-interpreter-interactive-arg) + ;; Try to cleanup + (delete-file code-file))) + (buffer-string))) + (prompts + (catch 'prompts + (dolist (line (split-string output "\n" t)) + (let ((res + ;; Check if current line is a valid JSON array + (and (string= (substring line 0 2) "[\"") + (ignore-errors + ;; Return prompts as a list, not vector + (append (json-read-from-string line) nil))))) + ;; The list must contain 3 strings, where the first + ;; is the input prompt, the second is the block + ;; prompt and the last one is the output prompt. The + ;; input prompt is the only one that can't be empty. + (when (and (= (length res) 3) + (cl-every #'stringp res) + (not (string= (car res) ""))) + (throw 'prompts res)))) + nil))) + (when (and (not prompts) + python-shell-prompt-detect-failure-warning) + (warn + (concat + "Python shell prompts cannot be detected.\n" + "If your emacs session hangs when starting python shells\n" + "recover with `keyboard-quit' and then try fixing the\n" + "interactive flag for your interpreter by adjusting the\n" + "`python-shell-interpreter-interactive-arg' or add regexps\n" + "matching shell prompts in the directory-local friendly vars:\n" + " + `python-shell-prompt-regexp'\n" + " + `python-shell-prompt-block-regexp'\n" + " + `python-shell-prompt-output-regexp'\n" + "Or alternatively in:\n" + " + `python-shell-prompt-input-regexps'\n" + " + `python-shell-prompt-output-regexps'"))) + prompts))) + +(defun python-shell-prompt-validate-regexps () + "Validate all user provided regexps for prompts. +Signals `user-error' if any of these vars contain invalid +regexps: `python-shell-prompt-regexp', +`python-shell-prompt-block-regexp', +`python-shell-prompt-pdb-regexp', +`python-shell-prompt-output-regexp', +`python-shell-prompt-input-regexps', +`python-shell-prompt-output-regexps'." + (dolist (symbol (list 'python-shell-prompt-input-regexps + 'python-shell-prompt-output-regexps + 'python-shell-prompt-regexp + 'python-shell-prompt-block-regexp + 'python-shell-prompt-pdb-regexp + 'python-shell-prompt-output-regexp)) + (dolist (regexp (let ((regexps (symbol-value symbol))) + (if (listp regexps) + regexps + (list regexps)))) + (when (not (python-util-valid-regexp-p regexp)) + (user-error "Invalid regexp %s in `%s'" + regexp symbol))))) + +(defun python-shell-prompt-set-calculated-regexps () + "Detect and set input and output prompt regexps. +Build and set the values for `python-shell-input-prompt-regexp' +and `python-shell-output-prompt-regexp' using the values from +`python-shell-prompt-regexp', `python-shell-prompt-block-regexp', +`python-shell-prompt-pdb-regexp', +`python-shell-prompt-output-regexp', +`python-shell-prompt-input-regexps', +`python-shell-prompt-output-regexps' and detected prompts from +`python-shell-prompt-detect'." + (when (not (and python-shell--prompt-calculated-input-regexp + python-shell--prompt-calculated-output-regexp)) + (let* ((detected-prompts (python-shell-prompt-detect)) + (input-prompts nil) + (output-prompts nil) + (build-regexp + (lambda (prompts) + (concat "^\\(" + (mapconcat #'identity + (sort prompts + (lambda (a b) + (let ((length-a (length a)) + (length-b (length b))) + (if (= length-a length-b) + (string< a b) + (> (length a) (length b)))))) + "\\|") + "\\)")))) + ;; Validate ALL regexps + (python-shell-prompt-validate-regexps) + ;; Collect all user defined input prompts + (dolist (prompt (append python-shell-prompt-input-regexps + (list python-shell-prompt-regexp + python-shell-prompt-block-regexp + python-shell-prompt-pdb-regexp))) + (cl-pushnew prompt input-prompts :test #'string=)) + ;; Collect all user defined output prompts + (dolist (prompt (cons python-shell-prompt-output-regexp + python-shell-prompt-output-regexps)) + (cl-pushnew prompt output-prompts :test #'string=)) + ;; Collect detected prompts if any + (when detected-prompts + (dolist (prompt (butlast detected-prompts)) + (setq prompt (regexp-quote prompt)) + (cl-pushnew prompt input-prompts :test #'string=)) + (cl-pushnew (regexp-quote + (car (last detected-prompts))) + output-prompts :test #'string=)) + ;; Set input and output prompt regexps from collected prompts + (setq python-shell--prompt-calculated-input-regexp + (funcall build-regexp input-prompts) + python-shell--prompt-calculated-output-regexp + (funcall build-regexp output-prompts))))) + (defun python-shell-get-process-name (dedicated) "Calculate the appropriate process name for inferior Python process. If DEDICATED is t and the variable `buffer-file-name' is non-nil @@ -1823,10 +2036,10 @@ uniqueness for different types of configurations." python-shell-internal-buffer-name (md5 (concat - (python-shell-parse-command) - python-shell-prompt-regexp - python-shell-prompt-block-regexp - python-shell-prompt-output-regexp + python-shell-interpreter + python-shell-interpreter-args + python-shell--prompt-calculated-input-regexp + python-shell--prompt-calculated-output-regexp (mapconcat #'symbol-value python-shell-setup-codes "") (mapconcat #'identity python-shell-process-environment "") (mapconcat #'identity python-shell-extra-pythonpaths "") @@ -1920,12 +2133,19 @@ 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)) + (let ((interpreter python-shell-interpreter) + (args python-shell-interpreter-args)) + (when python-shell--parent-buffer + (python-util-clone-local-variables python-shell--parent-buffer)) + ;; Users can override default values for these vars when calling + ;; `run-python'. This ensures new values let-bound in + ;; `python-shell-make-comint' are locally set. + (set (make-local-variable 'python-shell-interpreter) interpreter) + (set (make-local-variable 'python-shell-interpreter-args) args)) + (set (make-local-variable 'python-shell--prompt-calculated-input-regexp) nil) + (set (make-local-variable 'python-shell--prompt-calculated-output-regexp) nil) + (python-shell-prompt-set-calculated-regexps) + (setq comint-prompt-regexp python-shell--prompt-calculated-input-regexp) (setq mode-line-process '(":%s")) (make-local-variable 'comint-output-filter-functions) (add-hook 'comint-output-filter-functions @@ -1988,10 +2208,20 @@ killed." (exec-path (python-shell-calculate-exec-path))) (when (not (comint-check-proc proc-buffer-name)) (let* ((cmdlist (split-string-and-unquote cmd)) + (interpreter (car cmdlist)) + (args (cdr cmdlist)) (buffer (apply #'make-comint-in-buffer proc-name proc-buffer-name - (car cmdlist) nil (cdr cmdlist))) + interpreter nil args)) (python-shell--parent-buffer (current-buffer)) - (process (get-buffer-process buffer))) + (process (get-buffer-process buffer)) + ;; As the user may have overriden default values for + ;; these vars on `run-python', let-binding them allows + ;; to have the new right values in all setup code + ;; that's is done in `inferior-python-mode', which is + ;; important, especially for prompt detection. + (python-shell-interpreter interpreter) + (python-shell-interpreter-args + (mapconcat #'identity args " "))) (with-current-buffer buffer (inferior-python-mode)) (accept-process-output process) @@ -2063,8 +2293,12 @@ startup." "Return inferior Python process for current buffer." (get-buffer-process (python-shell-get-buffer))) -(defun python-shell-get-or-create-process () - "Get or create an inferior Python process for current buffer and return it." +(defun python-shell-get-or-create-process (&optional cmd dedicated show) + "Get or create an inferior Python process for current buffer and return it. +Arguments CMD, DEDICATED and SHOW are those of `run-python' and +are used to start the shell. If those arguments are not +provided, `run-python' is called interactively and the user will +be asked for their values." (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)) @@ -2072,7 +2306,11 @@ startup." (dedicated-running (comint-check-proc dedicated-proc-buffer-name)) (global-running (comint-check-proc global-proc-buffer-name))) (when (and (not dedicated-running) (not global-running)) - (if (call-interactively 'run-python) + (if (if (not cmd) + ;; XXX: Refactor code such that calling `run-python' + ;; interactively is not needed anymore. + (call-interactively 'run-python) + (run-python cmd dedicated show)) (setq dedicated-running t) (setq global-running t))) ;; Always prefer dedicated @@ -2155,10 +2393,13 @@ detecting a prompt at the end of the buffer." (when (string-match ;; 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) + (concat + "\r?\n" + ;; Remove initial caret from calculated regexp + (replace-regexp-in-string + (rx string-start ?^) "" + python-shell--prompt-calculated-input-regexp) + "$") python-shell-output-filter-buffer) ;; Output ends when `python-shell-output-filter-buffer' contains ;; the prompt attached at the end of it. @@ -2166,10 +2407,10 @@ detecting a prompt at the end of the buffer." 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 + (when (string-match + python-shell--prompt-calculated-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))))) @@ -2379,23 +2620,35 @@ This function takes the list of setup code to send from the (defcustom python-shell-completion-setup-code "try: - import readline + import readline, rlcompleter except ImportError: - def __COMPLETER_all_completions(text): [] + def __PYTHON_EL_get_completions(text): + return [] else: - import rlcompleter - readline.set_completer(rlcompleter.Completer().complete) - def __COMPLETER_all_completions(text): - import sys + def __PYTHON_EL_get_completions(text): completions = [] try: - i = 0 - while True: - res = readline.get_completer()(text, i) - if not res: break - i += 1 - completions.append(res) - except NameError: + splits = text.split() + is_module = splits and splits[0] in ('from', 'import') + is_ipython = getattr( + __builtins__, '__IPYTHON__', + getattr(__builtins__, '__IPYTHON__active', False)) + if is_module: + from IPython.core.completerlib import module_completion + completions = module_completion(text.strip()) + elif is_ipython and getattr(__builtins__, '__IP', None): + completions = __IP.complete(text) + elif is_ipython and getattr(__builtins__, 'get_ipython', None): + completions = get_ipython().Completer.all_completions(text) + else: + i = 0 + while True: + res = readline.get_completer()(text, i) + if not res: + break + i += 1 + completions.append(res) + except: pass return completions" "Code used to setup completion in inferior Python processes." @@ -2403,24 +2656,18 @@ else: :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." + "';'.join(__PYTHON_EL_get_completions('''%s'''))\n" + "Python code used to get a string of completions separated by semicolons. +The string passed to the function is the current python name or +the full statement in the case of imports." :type 'string :group 'python) -(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 -`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) +(define-obsolete-variable-alias + 'python-shell-completion-module-string-code + 'python-shell-completion-string-code + "24.4" + "Completion string code must also autocomplete modules.") (defcustom python-shell-completion-pdb-string-code "';'.join(globals().keys() + locals().keys())" @@ -2443,33 +2690,23 @@ LINE is used to detect the context on how to complete given INPUT." (re-search-backward "^") (python-util-forward-comment) (point)))))) - (completion-context + (completion-code ;; 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) + python-shell-completion-pdb-string-code) ((string-match - (concat "^" python-shell-prompt-regexp) prompt) - 'default) + python-shell--prompt-calculated-input-regexp prompt) + python-shell-completion-string-code) (t nil))) - (completion-code - (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) + (if (string-match + (python-rx (+ space) (or "from" "import") space) + line) + line input))) (and completion-code (> (length input) 0) @@ -3710,6 +3947,10 @@ returned as is." "" string)) +(defun python-util-valid-regexp-p (regexp) + "Return non-nil if REGEXP is valid." + (ignore-errors (string-match regexp "") t)) + (defun python-electric-pair-string-delimiter () (when (and electric-pair-mode @@ -3794,8 +4035,6 @@ returned as is." ,(lambda (_arg) (python-nav-end-of-defun)) 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) ":[^\n]*\n") diff --git a/lisp/view.el b/lisp/view.el index a5992ccb62a..9f914b03f56 100644 --- a/lisp/view.el +++ b/lisp/view.el @@ -491,12 +491,12 @@ Entry to view-mode runs the normal hook `view-mode-hook'." (defun view--disable () (remove-hook 'change-major-mode-hook 'view--disable t) (and view-overlay (delete-overlay view-overlay)) - ;; Calling toggle-read-only while View mode is enabled + ;; Calling read-only-mode while View mode is enabled ;; sets view-read-only to t as a buffer-local variable - ;; after exiting View mode. That arranges that the next toggle-read-only + ;; after exiting View mode. That arranges that the next read-only-mode ;; will reenable View mode. ;; Canceling View mode in any other way should cancel that, too, - ;; so that View mode stays off if toggle-read-only is called. + ;; so that View mode stays off if read-only-mode is called. (if (local-variable-p 'view-read-only) (kill-local-variable 'view-read-only)) (if (boundp 'Helper-return-blurb) diff --git a/src/ChangeLog b/src/ChangeLog index d4f6321faf7..25732ac9f5d 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,37 @@ +2014-07-21 Jan Djärv + + * nsterm.m (applicationDidFinishLaunching:): Call + antialiasThresholdDidChange, register for antialias changes (Bug#17534). + (antialiasThresholdDidChange:): New method for EmacsApp. + + * nsterm.h (EmacsApp): Add antialiasThresholdDidChange. + + * macfont.m (macfont_update_antialias_threshold): Remove static. + + * macfont.h (macfont_update_antialias_threshold): Declare. + +2014-07-21 Eli Zaretskii + + * w32select.c (setup_windows_coding_system): Apply + CODING_ANNOTATION_MASK to the common_flags member of struct + coding_system. Reported by martin rudalics . + + * w16select.c (Fw16_get_clipboard_data): Apply + CODING_ANNOTATION_MASK to the common_flags member of struct + coding_system. + + * xdisp.c (init_iterator): Initialize it->stop_charpos to the + buffer position where we are to start the iteration. + (handle_invisible_prop): Record in it->stop_charpos the position + where the invisible text ends. (Bug#18035) + (hscroll_window_tree): Don't try hscrolling windows whose cursor + row has zero buffer position as their start position. Reported by + martin rudalics . + + * xdisp.c (move_it_vertically_backward, move_it_by_lines): Prevent + infinite looping in redisplay when display lines don't have enough + space to display even a single character. (Bug#18036) + 2014-07-20 Dmitry Antipov * frame.h (struct frame) [USE_X_TOOLKIT]: New member shell_position. diff --git a/src/macfont.h b/src/macfont.h index 8b451357e42..7421cd63a79 100644 --- a/src/macfont.h +++ b/src/macfont.h @@ -144,4 +144,5 @@ typedef const struct _EmacsScreenFont *ScreenFontRef; /* opaque */ extern void mac_register_font_driver (struct frame *f); extern void *macfont_get_nsctfont (struct font *font); +extern void macfont_update_antialias_threshold (void); diff --git a/src/macfont.m b/src/macfont.m index 60f261d5549..0d702873220 100644 --- a/src/macfont.m +++ b/src/macfont.m @@ -728,7 +728,7 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char, static CGFloat macfont_antialias_threshold; -static void +void macfont_update_antialias_threshold (void) { int threshold; diff --git a/src/nsterm.h b/src/nsterm.h index 2619b710073..7ed23f583f6 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -111,6 +111,7 @@ typedef float EmacsCGFloat; #endif } - (void)logNotification: (NSNotification *)notification; +- (void)antialiasThresholdDidChange:(NSNotification *)notification; - (void)sendEvent: (NSEvent *)theEvent; - (void)showPreferencesWindow: (id)sender; - (BOOL) openFile: (NSString *)fileName; diff --git a/src/nsterm.m b/src/nsterm.m index 9420031645d..64f3be6cc06 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -4670,9 +4670,30 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification ((EmacsApp *)self)->applicationDidFinishLaunchingCalled = YES; #endif [NSApp setServicesProvider: NSApp]; + + [self antialiasThresholdDidChange:nil]; +#ifdef NS_IMPL_COCOA +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(antialiasThresholdDidChange:) + name:NSAntialiasThresholdChangedNotification + object:nil]; +#endif +#endif + ns_send_appdefined (-2); } +- (void)antialiasThresholdDidChange:(NSNotification *)notification +{ +#ifdef NS_IMPL_COCOA +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + macfont_update_antialias_threshold (); +#endif +#endif +} + /* Termination sequences: C-x C-c: diff --git a/src/w16select.c b/src/w16select.c index 1e4d35b721b..c229ba5816a 100644 --- a/src/w16select.c +++ b/src/w16select.c @@ -600,7 +600,7 @@ DEFUN ("w16-get-clipboard-data", Fw16_get_clipboard_data, Sw16_get_clipboard_dat coding.mode |= CODING_MODE_LAST_BLOCK; /* We explicitly disable composition handling because selection data should not contain any composition sequence. */ - coding.mode &= CODING_ANNOTATION_MASK; + coding.common_flags &= ~CODING_ANNOTATION_MASK; decode_coding_object (&coding, Qnil, 0, 0, truelen, truelen, Qt); ret = coding.dst_object; Vlast_coding_system_used = CODING_ID_NAME (coding.id); diff --git a/src/w32select.c b/src/w32select.c index 2ae25858bd2..7c21dde01a5 100644 --- a/src/w32select.c +++ b/src/w32select.c @@ -670,7 +670,7 @@ setup_windows_coding_system (Lisp_Object coding_system, which both apply to ISO6429 only. We don't know if these really need to be unset on Windows, but it probably doesn't hurt either. */ - coding->mode &= ~CODING_ANNOTATION_MASK; + coding->common_flags &= ~CODING_ANNOTATION_MASK; coding->mode |= CODING_MODE_LAST_BLOCK | CODING_MODE_SAFE_ENCODING; } diff --git a/src/xdisp.c b/src/xdisp.c index f1e01146e9f..a340c6e00dd 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3034,6 +3034,7 @@ init_iterator (struct it *it, struct window *w, getting overlays and face properties from that position. */ if (charpos >= BUF_BEG (current_buffer)) { + it->stop_charpos = charpos; it->end_charpos = ZV; eassert (charpos == BYTE_TO_CHAR (bytepos)); IT_CHARPOS (*it) = charpos; @@ -4546,7 +4547,24 @@ handle_invisible_prop (struct it *it) && get_overlay_strings (it, it->stop_charpos)) { handled = HANDLED_RECOMPUTE_PROPS; - it->stack[it->sp - 1].display_ellipsis_p = display_ellipsis_p; + if (it->sp > 0) + { + it->stack[it->sp - 1].display_ellipsis_p = display_ellipsis_p; + /* The call to get_overlay_strings above recomputes + it->stop_charpos, but it only considers changes + in properties and overlays beyond iterator's + current position. This causes us to miss changes + that happen exactly where the invisible property + ended. So we play it safe here and force the + iterator to check for potential stop positions + immediately after the invisible text. Note that + if get_overlay_strings returns non-zero, it + normally also pushed the iterator stack, so we + need to update the stop position in the slot + below the current one. */ + it->stack[it->sp - 1].stop_charpos + = CHARPOS (it->stack[it->sp - 1].current.pos); + } } else if (display_ellipsis_p) { @@ -9351,7 +9369,7 @@ move_it_vertically_backward (struct it *it, int dy) /* Estimate how many newlines we must move back. */ nlines = max (1, dy / default_line_pixel_height (it->w)); - if (it->line_wrap == TRUNCATE) + if (it->line_wrap == TRUNCATE || nchars_per_row == 0) pos_limit = BEGV; else pos_limit = max (start_pos - nlines * nchars_per_row, BEGV); @@ -9606,7 +9624,7 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos) /* Go back -DVPOS buffer lines, but no farther than -DVPOS full screen lines, and reseat the iterator there. */ start_charpos = IT_CHARPOS (*it); - if (it->line_wrap == TRUNCATE) + if (it->line_wrap == TRUNCATE || nchars_per_row == 0) pos_limit = BEGV; else pos_limit = max (start_charpos + dvpos * nchars_per_row, BEGV); @@ -12847,6 +12865,13 @@ hscroll_window_tree (Lisp_Object window) h_margin = hscroll_margin * WINDOW_FRAME_COLUMN_WIDTH (w); if (!NILP (Fbuffer_local_value (Qauto_hscroll_mode, w->contents)) + /* In some pathological cases, like restoring a window + configuration into a frame that is much smaller than + the one from which the configuration was saved, we + get glyph rows whose start and end have zero buffer + positions, which we cannot handle below. Just skip + such windows. */ + && CHARPOS (cursor_row->start.pos) >= BUF_BEG (w->contents) /* For left-to-right rows, hscroll when cursor is either (i) inside the right hscroll margin, or (ii) if it is inside the left margin and the window is already diff --git a/test/ChangeLog b/test/ChangeLog index dca2f7e14fe..c66ea1c739f 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,37 @@ +2014-07-21 Fabián Ezequiel Gallina + + * automated/python-tests.el: + (python-util-clone-local-variables-1): Fix test. + + * automated/python-tests.el (python-shell-make-comint-1): + (python-shell-make-comint-2): Fix indentation. + (python-shell-make-comint-3) + (python-shell-make-comint-4): New tests. + (python-shell-get-or-create-process-1): Fix test. + (python-shell-get-or-create-process-2) + (python-shell-get-or-create-process-3): New tests. + (python-shell-internal-get-or-create-process-1): Fix test. + (python-shell-prompt-detect-1): New test. + (python-shell-prompt-detect-2): New test. (Bug#17370) + (python-shell-prompt-detect-3) + (python-shell-prompt-detect-4) + (python-shell-prompt-detect-5) + (python-shell-prompt-detect-6) + (python-shell-prompt-validate-regexps-1) + (python-shell-prompt-validate-regexps-2) + (python-shell-prompt-validate-regexps-3) + (python-shell-prompt-validate-regexps-4) + (python-shell-prompt-validate-regexps-5) + (python-shell-prompt-validate-regexps-6) + (python-shell-prompt-validate-regexps-7) + (python-shell-prompt-set-calculated-regexps-1) + (python-shell-prompt-set-calculated-regexps-2) + (python-shell-prompt-set-calculated-regexps-3) + (python-shell-prompt-set-calculated-regexps-4) + (python-shell-prompt-set-calculated-regexps-5) + (python-shell-prompt-set-calculated-regexps-6) + (python-util-valid-regexp-p-1): New tests. + 2014-07-21 Stefan Monnier * automated/advice-tests.el (advice-test-call-interactively): Make sure @@ -48,7 +82,7 @@ * automated/python-tests.el (python-tests-self-insert): New function. (python-triple-quote-pairing): Use it. - (python-util-forward-comment-1): New test. (Bug#17658) + (python-parens-electric-indent-1): New test. (Bug#17658) 2014-06-30 Fabián Ezequiel Gallina diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el index 3a4eda36bfe..81f4567091e 100644 --- a/test/automated/python-tests.el +++ b/test/automated/python-tests.el @@ -1773,8 +1773,8 @@ Using `python-shell-interpreter' and (proc-name (python-shell-get-process-name nil)) (shell-buffer (python-tests-with-temp-buffer - "" (python-shell-make-comint - (python-shell-parse-command) proc-name))) + "" (python-shell-make-comint + (python-shell-parse-command) proc-name))) (process (get-buffer-process shell-buffer))) (unwind-protect (progn @@ -1794,8 +1794,8 @@ Using `python-shell-interpreter' and (proc-name (python-shell-internal-get-process-name)) (shell-buffer (python-tests-with-temp-buffer - "" (python-shell-make-comint - (python-shell-parse-command) proc-name nil t))) + "" (python-shell-make-comint + (python-shell-parse-command) proc-name nil t))) (process (get-buffer-process shell-buffer))) (unwind-protect (progn @@ -1806,6 +1806,79 @@ Using `python-shell-interpreter' and (should (string= (buffer-name) (format " *%s*" proc-name))))) (kill-buffer shell-buffer)))) +(ert-deftest python-shell-make-comint-3 () + "Check comint creation with overriden python interpreter and args. +The command passed to `python-shell-make-comint' as argument must +locally override global values set in `python-shell-interpreter' +and `python-shell-interpreter-args' in the new shell buffer." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((python-shell-setup-codes nil) + (python-shell-interpreter "interpreter") + (python-shell-interpreter-args "--some-args") + (proc-name (python-shell-get-process-name nil)) + (interpreter-override + (concat (executable-find python-tests-shell-interpreter) " " "-i")) + (shell-buffer + (python-tests-with-temp-buffer + "" (python-shell-make-comint interpreter-override proc-name nil))) + (process (get-buffer-process shell-buffer))) + (unwind-protect + (progn + (set-process-query-on-exit-flag process nil) + (should (process-live-p process)) + (with-current-buffer shell-buffer + (should (eq major-mode 'inferior-python-mode)) + (should (string= python-shell-interpreter + (executable-find python-tests-shell-interpreter))) + (should (string= python-shell-interpreter-args "-i")))) + (kill-buffer shell-buffer)))) + +(ert-deftest python-shell-make-comint-4 () + "Check shell calculated prompts regexps are set." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((process-environment process-environment) + (python-shell-setup-codes nil) + (python-shell-interpreter + (executable-find python-tests-shell-interpreter)) + (python-shell-interpreter-args "-i") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled t) + (python-shell-prompt-input-regexps '("extralargeinputprompt" "sml")) + (python-shell-prompt-output-regexps '("extralargeoutputprompt" "sml")) + (python-shell-prompt-regexp "in") + (python-shell-prompt-block-regexp "block") + (python-shell-prompt-pdb-regexp "pdf") + (python-shell-prompt-output-regexp "output") + (startup-code (concat "import sys\n" + "sys.ps1 = 'py> '\n" + "sys.ps2 = '..> '\n" + "sys.ps3 = 'out '\n")) + (startup-file (python-shell--save-temp-file startup-code)) + (proc-name (python-shell-get-process-name nil)) + (shell-buffer + (progn + (setenv "PYTHONSTARTUP" startup-file) + (python-tests-with-temp-buffer + "" (python-shell-make-comint + (python-shell-parse-command) proc-name nil)))) + (process (get-buffer-process shell-buffer))) + (unwind-protect + (progn + (set-process-query-on-exit-flag process nil) + (should (process-live-p process)) + (with-current-buffer shell-buffer + (should (eq major-mode 'inferior-python-mode)) + (should (string= + python-shell--prompt-calculated-input-regexp + (concat "^\\(extralargeinputprompt\\|\\.\\.> \\|" + "block\\|py> \\|pdf\\|sml\\|in\\)"))) + (should (string= + python-shell--prompt-calculated-output-regexp + "^\\(extralargeoutputprompt\\|output\\|out \\|sml\\)")))) + (delete-file startup-file) + (kill-buffer shell-buffer)))) + (ert-deftest python-shell-get-process-1 () "Check dedicated shell process preference over global." (skip-unless (executable-find python-tests-shell-interpreter)) @@ -1840,54 +1913,370 @@ Using `python-shell-interpreter' and (ignore-errors (kill-buffer dedicated-shell-buffer)))))) (ert-deftest python-shell-get-or-create-process-1 () - "Check shell process creation fallback." - :expected-result :failed + "Check shell dedicated process creation." + (skip-unless (executable-find python-tests-shell-interpreter)) (python-tests-with-temp-file - "" - ;; XXX: Break early until we can skip stuff. We need to mimic - ;; user interaction because `python-shell-get-or-create-process' - ;; asks for all arguments interactively when a shell process - ;; doesn't exist. - (should nil) - (let* ((python-shell-interpreter - (executable-find python-tests-shell-interpreter)) - (use-dialog-box) - (dedicated-process-name (python-shell-get-process-name t)) - (dedicated-process (python-shell-get-or-create-process)) - (dedicated-shell-buffer (process-buffer dedicated-process))) - (unwind-protect - (progn - (set-process-query-on-exit-flag dedicated-process nil) - ;; Prefer dedicated if not buffer exist. - (should (equal (process-name dedicated-process) - dedicated-process-name)) - (kill-buffer dedicated-shell-buffer) - ;; No buffer available. - (should (not (python-shell-get-process)))) - (ignore-errors (kill-buffer dedicated-shell-buffer)))))) + "" + (let* ((python-shell-interpreter + (executable-find python-tests-shell-interpreter)) + (use-dialog-box) + (dedicated-process-name (python-shell-get-process-name t)) + (dedicated-process + (python-shell-get-or-create-process python-shell-interpreter t)) + (dedicated-shell-buffer (process-buffer dedicated-process))) + (unwind-protect + (progn + (set-process-query-on-exit-flag dedicated-process nil) + ;; should be dedicated. + (should (equal (process-name dedicated-process) + dedicated-process-name)) + (kill-buffer dedicated-shell-buffer) + ;; Check there are no processes for current buffer. + (should (not (python-shell-get-process)))) + (ignore-errors (kill-buffer dedicated-shell-buffer)))))) + +(ert-deftest python-shell-get-or-create-process-2 () + "Check shell global process creation." + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-file + "" + (let* ((python-shell-interpreter + (executable-find python-tests-shell-interpreter)) + (use-dialog-box) + (process-name (python-shell-get-process-name nil)) + (process + (python-shell-get-or-create-process python-shell-interpreter)) + (shell-buffer (process-buffer process))) + (unwind-protect + (progn + (set-process-query-on-exit-flag process nil) + ;; should be global. + (should (equal (process-name process) process-name)) + (kill-buffer shell-buffer) + ;; Check there are no processes for current buffer. + (should (not (python-shell-get-process)))) + (ignore-errors (kill-buffer dedicated-shell-buffer)))))) + +(ert-deftest python-shell-get-or-create-process-3 () + "Check shell dedicated/global process preference." + (skip-unless (executable-find python-tests-shell-interpreter)) + (python-tests-with-temp-file + "" + (let* ((python-shell-interpreter + (executable-find python-tests-shell-interpreter)) + (use-dialog-box) + (dedicated-process-name (python-shell-get-process-name t)) + (global-process) + (dedicated-process)) + (unwind-protect + (progn + ;; Create global process + (run-python python-shell-interpreter nil) + (setq global-process (get-buffer-process "*Python*")) + (should global-process) + (set-process-query-on-exit-flag global-process nil) + ;; Create dedicated process + (run-python python-shell-interpreter t) + (setq dedicated-process (get-process dedicated-process-name)) + (should dedicated-process) + (set-process-query-on-exit-flag dedicated-process nil) + ;; Prefer dedicated. + (should (equal (python-shell-get-or-create-process) + dedicated-process)) + ;; Kill the dedicated so the global takes over. + (kill-buffer (process-buffer dedicated-process)) + ;; Detect global. + (should (equal (python-shell-get-or-create-process) global-process)) + ;; Kill the global. + (kill-buffer (process-buffer global-process)) + ;; Check there are no processes for current buffer. + (should (not (python-shell-get-process)))) + (ignore-errors (kill-buffer dedicated-shell-buffer)))))) (ert-deftest python-shell-internal-get-or-create-process-1 () "Check internal shell process creation fallback." (skip-unless (executable-find python-tests-shell-interpreter)) (python-tests-with-temp-file - "" - (should (not (process-live-p (python-shell-internal-get-process-name)))) - (let* ((python-shell-interpreter - (executable-find python-tests-shell-interpreter)) - (internal-process-name (python-shell-internal-get-process-name)) - (internal-process (python-shell-internal-get-or-create-process)) - (internal-shell-buffer (process-buffer internal-process))) - (unwind-protect - (progn - (set-process-query-on-exit-flag internal-process nil) - (should (equal (process-name internal-process) - internal-process-name)) - (should (equal internal-process - (python-shell-internal-get-or-create-process))) - ;; No user buffer available. - (should (not (python-shell-get-process))) - (kill-buffer internal-shell-buffer)) - (ignore-errors (kill-buffer internal-shell-buffer)))))) + "" + (should (not (process-live-p (python-shell-internal-get-process-name)))) + (let* ((python-shell-interpreter + (executable-find python-tests-shell-interpreter)) + (internal-process-name (python-shell-internal-get-process-name)) + (internal-process (python-shell-internal-get-or-create-process)) + (internal-shell-buffer (process-buffer internal-process))) + (unwind-protect + (progn + (set-process-query-on-exit-flag internal-process nil) + (should (equal (process-name internal-process) + internal-process-name)) + (should (equal internal-process + (python-shell-internal-get-or-create-process))) + ;; Assert the internal process is not a user process + (should (not (python-shell-get-process))) + (kill-buffer internal-shell-buffer)) + (ignore-errors (kill-buffer internal-shell-buffer)))))) + +(ert-deftest python-shell-prompt-detect-1 () + "Check prompt autodetection." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let ((process-environment process-environment)) + ;; Ensure no startup file is enabled + (setenv "PYTHONSTARTUP" "") + (should python-shell-prompt-detect-enabled) + (should (equal (python-shell-prompt-detect) '(">>> " "... " ""))))) + +(ert-deftest python-shell-prompt-detect-2 () + "Check prompt autodetection with startup file. Bug#17370." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((process-environment process-environment) + (startup-code (concat "import sys\n" + "sys.ps1 = 'py> '\n" + "sys.ps2 = '..> '\n" + "sys.ps3 = 'out '\n")) + (startup-file (python-shell--save-temp-file startup-code))) + (unwind-protect + (progn + ;; Ensure startup file is enabled + (setenv "PYTHONSTARTUP" startup-file) + (should python-shell-prompt-detect-enabled) + (should (equal (python-shell-prompt-detect) '("py> " "..> " "out ")))) + (ignore-errors (delete-file startup-file))))) + +(ert-deftest python-shell-prompt-detect-3 () + "Check prompts are not autodetected when feature is disabled." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let ((process-environment process-environment) + (python-shell-prompt-detect-enabled nil)) + ;; Ensure no startup file is enabled + (should (not python-shell-prompt-detect-enabled)) + (should (not (python-shell-prompt-detect))))) + +(ert-deftest python-shell-prompt-detect-4 () + "Check warning is shown when detection fails." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((process-environment process-environment) + ;; Trigger failure by removing prompts in the startup file + (startup-code (concat "import sys\n" + "sys.ps1 = ''\n" + "sys.ps2 = ''\n" + "sys.ps3 = ''\n")) + (startup-file (python-shell--save-temp-file startup-code))) + (unwind-protect + (progn + (kill-buffer (get-buffer-create "*Warnings*")) + (should (not (get-buffer "*Warnings*"))) + (setenv "PYTHONSTARTUP" startup-file) + (should python-shell-prompt-detect-failure-warning) + (should python-shell-prompt-detect-enabled) + (should (not (python-shell-prompt-detect))) + (should (get-buffer "*Warnings*"))) + (ignore-errors (delete-file startup-file))))) + +(ert-deftest python-shell-prompt-detect-5 () + "Check disabled warnings are not shown when detection fails." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((process-environment process-environment) + (startup-code (concat "import sys\n" + "sys.ps1 = ''\n" + "sys.ps2 = ''\n" + "sys.ps3 = ''\n")) + (startup-file (python-shell--save-temp-file startup-code)) + (python-shell-prompt-detect-failure-warning nil)) + (unwind-protect + (progn + (kill-buffer (get-buffer-create "*Warnings*")) + (should (not (get-buffer "*Warnings*"))) + (setenv "PYTHONSTARTUP" startup-file) + (should (not python-shell-prompt-detect-failure-warning)) + (should python-shell-prompt-detect-enabled) + (should (not (python-shell-prompt-detect))) + (should (not (get-buffer "*Warnings*")))) + (ignore-errors (delete-file startup-file))))) + +(ert-deftest python-shell-prompt-detect-6 () + "Warnings are not shown when detection is disabled." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((process-environment process-environment) + (startup-code (concat "import sys\n" + "sys.ps1 = ''\n" + "sys.ps2 = ''\n" + "sys.ps3 = ''\n")) + (startup-file (python-shell--save-temp-file startup-code)) + (python-shell-prompt-detect-failure-warning t) + (python-shell-prompt-detect-enabled nil)) + (unwind-protect + (progn + (kill-buffer (get-buffer-create "*Warnings*")) + (should (not (get-buffer "*Warnings*"))) + (setenv "PYTHONSTARTUP" startup-file) + (should python-shell-prompt-detect-failure-warning) + (should (not python-shell-prompt-detect-enabled)) + (should (not (python-shell-prompt-detect))) + (should (not (get-buffer "*Warnings*")))) + (ignore-errors (delete-file startup-file))))) + +(ert-deftest python-shell-prompt-validate-regexps-1 () + "Check `python-shell-prompt-input-regexps' are validated." + (let* ((python-shell-prompt-input-regexps '("\\(")) + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-input-regexps'")))) + +(ert-deftest python-shell-prompt-validate-regexps-2 () + "Check `python-shell-prompt-output-regexps' are validated." + (let* ((python-shell-prompt-output-regexps '("\\(")) + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-output-regexps'")))) + +(ert-deftest python-shell-prompt-validate-regexps-3 () + "Check `python-shell-prompt-regexp' is validated." + (let* ((python-shell-prompt-regexp "\\(") + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-regexp'")))) + +(ert-deftest python-shell-prompt-validate-regexps-4 () + "Check `python-shell-prompt-block-regexp' is validated." + (let* ((python-shell-prompt-block-regexp "\\(") + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-block-regexp'")))) + +(ert-deftest python-shell-prompt-validate-regexps-5 () + "Check `python-shell-prompt-pdb-regexp' is validated." + (let* ((python-shell-prompt-pdb-regexp "\\(") + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-pdb-regexp'")))) + +(ert-deftest python-shell-prompt-validate-regexps-6 () + "Check `python-shell-prompt-output-regexp' is validated." + (let* ((python-shell-prompt-output-regexp "\\(") + (error-data (should-error (python-shell-prompt-validate-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-output-regexp'")))) + +(ert-deftest python-shell-prompt-validate-regexps-7 () + "Check default regexps are valid." + ;; should not signal error + (python-shell-prompt-validate-regexps)) + +(ert-deftest python-shell-prompt-set-calculated-regexps-1 () + "Check regexps are validated." + (let* ((python-shell-prompt-output-regexp '("\\(")) + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled nil) + (error-data (should-error (python-shell-prompt-set-calculated-regexps) + :type 'user-error))) + (should + (string= (cadr error-data) + "Invalid regexp \\( in `python-shell-prompt-output-regexp'")))) + +(ert-deftest python-shell-prompt-set-calculated-regexps-2 () + "Check `python-shell-prompt-input-regexps' are set." + (let* ((python-shell-prompt-input-regexps '("my" "prompt")) + (python-shell-prompt-output-regexps '("")) + (python-shell-prompt-regexp "") + (python-shell-prompt-block-regexp "") + (python-shell-prompt-pdb-regexp "") + (python-shell-prompt-output-regexp "") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled nil)) + (python-shell-prompt-set-calculated-regexps) + (should (string= python-shell--prompt-calculated-input-regexp + "^\\(prompt\\|my\\|\\)")))) + +(ert-deftest python-shell-prompt-set-calculated-regexps-3 () + "Check `python-shell-prompt-output-regexps' are set." + (let* ((python-shell-prompt-input-regexps '("")) + (python-shell-prompt-output-regexps '("my" "prompt")) + (python-shell-prompt-regexp "") + (python-shell-prompt-block-regexp "") + (python-shell-prompt-pdb-regexp "") + (python-shell-prompt-output-regexp "") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled nil)) + (python-shell-prompt-set-calculated-regexps) + (should (string= python-shell--prompt-calculated-output-regexp + "^\\(prompt\\|my\\|\\)")))) + +(ert-deftest python-shell-prompt-set-calculated-regexps-4 () + "Check user defined prompts are set." + (let* ((python-shell-prompt-input-regexps '("")) + (python-shell-prompt-output-regexps '("")) + (python-shell-prompt-regexp "prompt") + (python-shell-prompt-block-regexp "block") + (python-shell-prompt-pdb-regexp "pdb") + (python-shell-prompt-output-regexp "output") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled nil)) + (python-shell-prompt-set-calculated-regexps) + (should (string= python-shell--prompt-calculated-input-regexp + "^\\(prompt\\|block\\|pdb\\|\\)")) + (should (string= python-shell--prompt-calculated-output-regexp + "^\\(output\\|\\)")))) + +(ert-deftest python-shell-prompt-set-calculated-regexps-5 () + "Check order of regexps (larger first)." + (let* ((python-shell-prompt-input-regexps '("extralargeinputprompt" "sml")) + (python-shell-prompt-output-regexps '("extralargeoutputprompt" "sml")) + (python-shell-prompt-regexp "in") + (python-shell-prompt-block-regexp "block") + (python-shell-prompt-pdb-regexp "pdf") + (python-shell-prompt-output-regexp "output") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled nil)) + (python-shell-prompt-set-calculated-regexps) + (should (string= python-shell--prompt-calculated-input-regexp + "^\\(extralargeinputprompt\\|block\\|pdf\\|sml\\|in\\)")) + (should (string= python-shell--prompt-calculated-output-regexp + "^\\(extralargeoutputprompt\\|output\\|sml\\)")))) + +(ert-deftest python-shell-prompt-set-calculated-regexps-6 () + "Check detected prompts are included `regexp-quote'd." + (skip-unless (executable-find python-tests-shell-interpreter)) + (let* ((python-shell-prompt-input-regexps '("")) + (python-shell-prompt-output-regexps '("")) + (python-shell-prompt-regexp "") + (python-shell-prompt-block-regexp "") + (python-shell-prompt-pdb-regexp "") + (python-shell-prompt-output-regexp "") + (python-shell--prompt-calculated-input-regexp nil) + (python-shell--prompt-calculated-output-regexp nil) + (python-shell-prompt-detect-enabled t) + (process-environment process-environment) + (startup-code (concat "import sys\n" + "sys.ps1 = 'p.> '\n" + "sys.ps2 = '..> '\n" + "sys.ps3 = 'o.t '\n")) + (startup-file (python-shell--save-temp-file startup-code))) + (unwind-protect + (progn + (setenv "PYTHONSTARTUP" startup-file) + (python-shell-prompt-set-calculated-regexps) + (should (string= python-shell--prompt-calculated-input-regexp + "^\\(\\.\\.> \\|p\\.> \\|\\)")) + (should (string= python-shell--prompt-calculated-output-regexp + "^\\(o\\.t \\|\\)"))) + (ignore-errors (delete-file startup-file))))) ;;; Shell completion @@ -3230,8 +3619,6 @@ def foo(a, b, c): (python-shell-extra-pythonpaths "/home/user/pylib/") (python-shell-completion-setup-code . "from IPython.core.completerlib import module_completion") - (python-shell-completion-module-string-code - . "';'.join(module_completion('''%s'''))\n") (python-shell-completion-string-code . "';'.join(get_ipython().Completer.all_completions('''%s'''))\n") (python-shell-virtualenv-path @@ -3269,6 +3656,11 @@ def foo(a, b, c): (python-util-forward-comment -1) (should (= (point) (point-min))))) +(ert-deftest python-util-valid-regexp-p-1 () + (should (python-util-valid-regexp-p "")) + (should (python-util-valid-regexp-p python-shell-prompt-regexp)) + (should (not (python-util-valid-regexp-p "\\(")))) + ;;; Electricity