From 7174b34701cb201c088f0fce6a57aee9a75eef20 Mon Sep 17 00:00:00 2001 From: Augusto Stoffel Date: Thu, 4 Aug 2022 12:08:58 +0200 Subject: [PATCH] Python shells dedicated to a project These shells run in the project root directory and are shared among all project buffers. * lisp/progmodes/python.el: Require 'seq' and (optionally) 'project' libraries. (python-shell-dedicated): New user option (python-shell-get-process-name): Adapt to project-dedicated shells. (run-python): Offer possibility to create a project-dedicated shell, or use 'python-shell-dedicated' as the default behavior. (python-shell-get-buffer): Adapt to project-dedicated shells. --- etc/NEWS | 11 ++++++ lisp/progmodes/python.el | 79 ++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 963aa22c68..fcbf7deba7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2055,6 +2055,17 @@ the Galeon web browser was released in September, 2008. Note that this historical web browser is different from Mozilla Firefox; it is its predecessor. +** Python Mode ++++ +*** Project shells and a new user option 'python-shell-dedicated' +When called with a prefix argument, 'run-python' now offers the choice +of creating a shell dedicated to the current project. This shell runs +in the project root directory and is shared among all project buffers. + +Without a prefix argument, the kind of shell (buffer-dedicated, +project-dedicated or global) is specified by the new +'python-shell-dedicated' variable. + ** Ruby Mode --- diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index b8fc7d4c54..bbdd2a9502 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -5,7 +5,7 @@ ;; Author: Fabián E. Gallina ;; URL: https://github.com/fgallina/python.el ;; Version: 0.28 -;; Package-Requires: ((emacs "24.4") (cl-lib "1.0")) +;; Package-Requires: ((emacs "24.4") (cl-lib "1.0") (seq "2.23")) ;; Maintainer: emacs-devel@gnu.org ;; Created: Jul 2010 ;; Keywords: languages @@ -245,6 +245,8 @@ (require 'ansi-color) (require 'cl-lib) (require 'comint) +(require 'project nil 'noerror) +(require 'seq) (eval-when-compile (require 'subr-x)) ;For `string-empty-p'. ;; Avoid compiler warnings @@ -2218,6 +2220,16 @@ python-shell-compilation-regexp-alist "`compilation-error-regexp-alist' for inferior Python." :type '(alist regexp)) +(defcustom python-shell-dedicated nil + "Whether to make Python shells dedicated by default. +This option influences `run-python' when called without a prefix +argument. If `buffer' or `project', create a Python shell +dedicated to the current buffer or its project (if one is found)." + :version "29.1" + :type '(choice (const :tag "To buffer" buffer) + (const :tag "To project" project) + (const :tag "Not dedicated" nil))) + (defvar python-shell-output-filter-in-progress nil) (defvar python-shell-output-filter-buffer nil) @@ -2580,12 +2592,19 @@ python-shell-prompt-set-calculated-regexps (defun python-shell-get-process-name (dedicated) "Calculate the appropriate process name for inferior Python process. -If DEDICATED is t returns a string with the form -`python-shell-buffer-name'[`buffer-name'] else returns the value -of `python-shell-buffer-name'." - (if dedicated - (format "%s[%s]" python-shell-buffer-name (buffer-name)) - python-shell-buffer-name)) +If DEDICATED is nil, this is simply `python-shell-buffer-name'. +If DEDICATED is `buffer' or `project', append the current buffer +name respectively the current project name." + (pcase dedicated + ('nil python-shell-buffer-name) + ('project + (if-let ((proj (and (featurep 'project) + (project-current)))) + (format "%s[%s]" python-shell-buffer-name (file-name-nondirectory + (directory-file-name + (project-root proj)))) + python-shell-buffer-name)) + (_ (format "%s[%s]" python-shell-buffer-name (buffer-name))))) (defun python-shell-internal-get-process-name () "Calculate the appropriate process name for Internal Python process. @@ -3043,8 +3062,8 @@ run-python Argument CMD defaults to `python-shell-calculate-command' return value. When called interactively with `prefix-arg', it allows the user to edit such value and choose whether the interpreter -should be DEDICATED for the current buffer. When numeric prefix -arg is other than 0 or 4 do not SHOW. +should be DEDICATED to the current buffer or project. When +numeric prefix arg is other than 0 or 4 do not SHOW. For a given buffer and same values of DEDICATED, if a process is already running for it, it will do nothing. This means that if @@ -3058,13 +3077,27 @@ run-python (if current-prefix-arg (list (read-shell-command "Run Python: " (python-shell-calculate-command)) - (y-or-n-p "Make dedicated process? ") + (if (not (fboundp 'read-multiple-choice)) + (y-or-n-p "Make dedicated process? ") + (alist-get (car (read-multiple-choice "Make dedicated process?" + '((?b "to buffer") + (?p "to project") + (?n "no")))) + '((?b . buffer) (?p . project)))) (= (prefix-numeric-value current-prefix-arg) 4)) - (list (python-shell-calculate-command) nil t))) - (let ((buffer - (python-shell-make-comint - (or cmd (python-shell-calculate-command)) - (python-shell-get-process-name dedicated) show))) + (list (python-shell-calculate-command) + python-shell-dedicated + t))) + (let* ((project (and (eq 'project dedicated) + (featurep 'project) + (project-current t))) + (default-directory (if project + (project-root project) + default-directory)) + (buffer (python-shell-make-comint + (or cmd (python-shell-calculate-command)) + (python-shell-get-process-name dedicated) + show))) (get-buffer-process buffer))) (defun run-python-internal () @@ -3094,15 +3127,13 @@ python-shell-get-buffer If current buffer is in `inferior-python-mode', return it." (if (derived-mode-p 'inferior-python-mode) (current-buffer) - (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 - (or (and dedicated-running dedicated-proc-buffer-name) - (and global-running global-proc-buffer-name))))) + (seq-some + (lambda (dedicated) + (let* ((proc-name (python-shell-get-process-name dedicated)) + (buffer-name (format "*%s*" proc-name))) + (when (comint-check-proc buffer-name) + buffer-name))) + '(buffer project nil)))) (defun python-shell-get-process () "Return inferior Python process for current buffer." -- 2.37.1