rename project

This commit is contained in:
Valentin Boettcher 2024-09-12 11:31:13 -04:00
parent fca6fd0851
commit 57d5bf3f62
No known key found for this signature in database
GPG key ID: E034E12B7AF56ACE
3 changed files with 234 additions and 234 deletions

View file

@ -6,7 +6,7 @@ and play nicely with =ipython=.
See also [[https://github.com/shg/python-vterm.el][the original author's efforts]].
* python-vterm
* py-vterm-interaction
Python-vterm provides a major-mode for an inferior Python process (or REPL) that runs in vterm, and a minor-mode that extends python-mode with the ability to interact with the inferior Python process. This is particularly useful for *fancy repls like =ipython= or =ptpython=*.
@ -19,44 +19,44 @@ that information.
Currently you can only install the package from github. With
~use-package~ and ~straight~ it looks as follows:
#+begin_src elisp
(use-package python-vterm
:straight (:host github :repo "vale981/python-vterm.el")
:hook (python-mode . python-vterm-mode)
(use-package py-vterm-interaction
:straight (:host github :repo "vale981/py-vterm-interaction.el")
:hook (python-mode . py-vterm-interaction-mode)
:config
(setq-default python-vterm-repl-program "ipython")
(setq-default python-vterm-silent-cells t))
(setq-default py-vterm-interaction-repl-program "ipython")
(setq-default py-vterm-interaction-silent-cells t))
#+end_src
For manual installation, download =python-vterm.el= into somewhere in your local directory and use =package-install-file= command. Please make sure [[https://github.com/PythonEditorSupport/python-emacs][python-mode]] and [[https://github.com/akermu/emacs-libvterm][emacs-libvterm]] are installed and configured correctly.
For manual installation, download =py-vterm-interaction.el= into somewhere in your local directory and use =package-install-file= command. Please make sure [[https://github.com/PythonEditorSupport/python-emacs][python-mode]] and [[https://github.com/akermu/emacs-libvterm][emacs-libvterm]] are installed and configured correctly.
Turn on =python-vterm-mode= in a =python-mode= buffer to use this package. A symbol “PY” in the mode line indicates that the python-mode buffer is ready to interact with the python-vterm REPL. Add the following line to your init file to enable =python-vterm-mode= in =python-mode= buffers automatically.
Turn on =py-vterm-interaction-mode= in a =python-mode= buffer to use this package. A symbol “PY” in the mode line indicates that the python-mode buffer is ready to interact with the py-vterm-interaction REPL. Add the following line to your init file to enable =py-vterm-interaction-mode= in =python-mode= buffers automatically.
#+BEGIN_SRC emacs-lisp
(add-hook 'python-mode-hook #'python-vterm-mode)
(add-hook 'python-mode-hook #'py-vterm-interaction-mode)
#+END_SRC
By default, the command named =python= in your =PATH= is used. You can use a Python executable in any path by setting the =python-vterm-repl-program= variable to its absolute path. The variable can contain switches for the =python= command. For example, you can use a =python= executable at a certain path, with 4 threads enabled, by the line like the following.
By default, the command named =python= in your =PATH= is used. You can use a Python executable in any path by setting the =py-vterm-interaction-repl-program= variable to its absolute path. The variable can contain switches for the =python= command. For example, you can use a =python= executable at a certain path, with 4 threads enabled, by the line like the following.
#+BEGIN_SRC emacs-lisp
(setq-default python-vterm-repl-program "ipython -i")
(setq-default py-vterm-interaction-repl-program "ipython -i")
#+END_SRC
Some commands (see below) can either paste code into the REPL verbatim
or include it using ~%run -i~. If ~python-vterm-silent-cells~ is set to ~t~
or include it using ~%run -i~. If ~py-vterm-interaction-silent-cells~ is set to ~t~
#+begin_src emacs-lisp
(setq-default python-vterm-silent-cells t)
(setq-default py-vterm-interaction-silent-cells t)
#+end_src
the latter option will be used.
** How to use
=M-x python-vterm-repl= (or =M-x python= if no other packages define it before python-vterm is loaded) opens an inferior Python REPL buffer.
=M-x py-vterm-interaction-repl= (or =M-x python= if no other packages define it before py-vterm-interaction is loaded) opens an inferior Python REPL buffer.
In a python script buffer with =python-vterm-mode= on, you can open a Python REPL with =M-x python-vterm-switch-to-repl-buffer= (or =C-c C-z=). See below for other commands.
In a python script buffer with =py-vterm-interaction-mode= on, you can open a Python REPL with =M-x py-vterm-interaction-switch-to-repl-buffer= (or =C-c C-z=). See below for other commands.
Both of the above operations open a REPL with the default session name =main=. You can specify a different session name by using the prefix argument =C-u=. A new session will be created and opened if there is no REPL with that session name.
You can also specify a session name by defining a file local variable =python-vterm-session= (or =python-session= if no other packages pre-define it). If the variable is defined, =C-c C-z= will open a REPL with that session name.
You can also specify a session name by defining a file local variable =py-vterm-interaction-session= (or =python-session= if no other packages pre-define it). If the variable is defined, =C-c C-z= will open a REPL with that session name.
The package also contains a clone of [[https://docs.spyder-ide.org/3/editor.html#defining-code-cells][spyder]]'s code cells. Consider the following:
#+begin_src python
@ -70,8 +70,8 @@ The package also contains a clone of [[https://docs.spyder-ide.org/3/editor.html
#+end_src
Moving the point between any two cell markers (or between the buffer
beginning/end and a marker) and executing
~python-vterm-send-current-cell~ (=C-c C-j=) will send the contents of the
block to the REPL. When setting ~python-vterm-silent-cells~ to ~t~, the
~py-vterm-interaction-send-current-cell~ (=C-c C-j=) will send the contents of the
block to the REPL. When setting ~py-vterm-interaction-silent-cells~ to ~t~, the
cell content is written to a temporary file which is then executed in
the REPL with the ~%run~ magic command.
@ -85,7 +85,7 @@ automatically call it if it has no arguments. Consider the following:
print("hi")
#+end_src
Moving the point inside of ~run_some_test~ and executing
~python-vterm-run-current-function~ (=C-c C-f=) will load the function
~py-vterm-interaction-run-current-function~ (=C-c C-f=) will load the function
into the REPL and execute it. For a function with arguments like
#+begin_src python
#... some code
@ -98,23 +98,23 @@ the function name and an opening parenthesis will be pasted into the reply.
** Key bindings
*** python-vterm-mode
*** py-vterm-interaction-mode
#+begin_example
Key Command / Description
------------------------------------------------------------------------------------------
C-c C-z python-vterm-switch-to-repl-buffer
C-c C-z py-vterm-interaction-switch-to-repl-buffer
Switch to the paired REPL buffer or to the one with a specified session name.
With prefix ARG, prompt for session name.
C-c C-c python-vterm-send-region-or-current-line
C-c C-c py-vterm-interaction-send-region-or-current-line
Send the content of the region if the region is active, or send the current
line.
C-c C-b python-vterm-send-buffer
C-c C-b py-vterm-interaction-send-buffer
Send the whole content of the script buffer to the Python REPL line by line.
C-c C-j python-vterm-send-current-cell
C-c C-j py-vterm-interaction-send-current-cell
Send the current code "cell" to the Python REPL.
Each block is delimited by `# %% <optional name>`.
@ -122,50 +122,50 @@ C-c C-j python-vterm-send-current-cell
begin with the buffer. Likewise, if there is no marker after the
point, the cell is assumed to end with the buffer.
C-c C-f python-vterm-run-current-function
C-c C-f py-vterm-interaction-run-current-function
Send the current function the Python REPL and paste its name, ready to run.
If the function has no arguments, the function call is run immediately.
C-c C-r python-vterm-send-run-buffer-file
C-c C-r py-vterm-interaction-send-run-buffer-file
Send a line to evaluate the buffer's file using ipython %run magic.
C-c C-i Import the current buffer file like `from <module> import *' in the python repl.
This is especially useful with `%autoload 3' in ipython.
C-c C-d python-vterm-send-cd-to-buffer-directory
C-c C-d py-vterm-interaction-send-cd-to-buffer-directory
Send %cd function call to the Python REPL to change the current working
directory of REPL to the buffer's directory.
#+end_example
*** python-vterm-repl-mode
*** py-vterm-interaction-repl-mode
#+begin_example
Key Command / Description
------------------------------------------------------------------------------------------
C-c C-z python-vterm-repl-switch-to-script-buffer
C-c C-z py-vterm-interaction-repl-switch-to-script-buffer
Switch to the script buffer that is paired with the current Python REPL buffer.
M-k python-vterm-repl-clear-buffer
M-k py-vterm-interaction-repl-clear-buffer
Clear the content of the Python REPL buffer.
C-c C-t python-vterm-repl-copy-mode
C-c C-t py-vterm-interaction-repl-copy-mode
Enter copy mode.
C-c M-r python-vterm-repl-restart
C-c M-r py-vterm-interaction-repl-restart
Restart the current inferior Python process. A new Python REPL will be opened in
the same Emacs window, and the working directory and environment will be
restored from the previous REPL process when possible.
#+end_example
*** python-vterm-repl-mode (copy mode)
*** py-vterm-interaction-repl-mode (copy mode)
#+begin_example
Key Command / Description
------------------------------------------------------------------------------------------
C-c C-t python-vterm-repl-copy-mode
C-c C-t py-vterm-interaction-repl-copy-mode
Exit copy mode.
<return> python-vterm-repl-copy-mode-done
<return> py-vterm-interaction-repl-copy-mode-done
Copy the region to the kill ring and exit copy mode.
C-c C-r vterm-reset-cursor-point

View file

@ -1,13 +1,13 @@
;;; python-vterm.el --- A mode for Python REPL using vterm -*- lexical-binding: t -*-
;;; py-vterm-interaction.el --- A mode for Python REPL using vterm -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Shigeaki Nishina
;; Copyright (C) 2020-2024 Shigeaki Nishina, Valentin Boettcher
;; Author: Shigeaki Nishina, Valentin Boettcher
;; Maintainer: Valentin Boettcher <hiro at protagon.space>
;; Created: May 11, 2024
;; URL: https://github.com/vale981/python-vterm.el
;; URL: https://github.com/vale981/python-fancy.el
;; Package-Requires: ((emacs "25.1") (vterm "0.0.1"))
;; Version: 1.0.6
;; Version: 1.0.0
;; Keywords: languages, python
;; This file is not part of GNU Emacs.
@ -40,14 +40,14 @@
;;; Usage:
;; You must have python-mode and vterm installed.
;; Install python-vterm.el manually using package.el
;; Install py-vterm-interaction.el manually using package.el
;;
;; (package-install-file "/path-to-download-dir/python-vterm.el")
;; (package-install-file "/path-to-download-dir/py-vterm-interaction.el")
;;
;; Eval the following line. Add this line to your init file to enable this
;; mode in future sessions.
;;
;; (add-hook 'python-mode-hook #'python-vterm-mode)
;; (add-hook 'python-mode-hook #'py-vterm-interaction-mode)
;;
;; Now you can interact with an inferior Python REPL from a Python buffer.
;;
@ -65,136 +65,136 @@
;;----------------------------------------------------------------------
(defgroup python-vterm-repl nil
(defgroup py-vterm-interaction-repl nil
"A major mode for inferior Python REPL."
:group 'python)
(defvar-local python-vterm-repl-program "python"
(defvar-local py-vterm-interaction-repl-program "python"
"Name of the command for executing Python code.
Maybe either a command in the path, like python
or an absolute path name, like /usr/local/bin/python
parameters may be used, like python -q")
(defvar-local python-vterm-silent-cells nil
(defvar-local py-vterm-interaction-silent-cells nil
"If non-nil, the PYTHON-VTERM-SEND-CURRENT-CELL will use ipythons `%run` magic to run a code cell.
For plain python `exec(open(...).read())` is used.")
(defvar-local python-vterm-repl-script-buffer nil)
(defvar-local python-vterm-repl-interpreter :python
(defvar-local py-vterm-interaction-repl-script-buffer nil)
(defvar-local py-vterm-interaction-repl-interpreter :python
"Reflects whether the inferior Python REPL is a Python or IPython shell.
If in doubt, set this to :python.")
(defvar python-vterm-repl-mode-map
(defvar py-vterm-interaction-repl-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-z") #'python-vterm-repl-switch-to-script-buffer)
(define-key map (kbd "M-k") #'python-vterm-repl-clear-buffer)
(define-key map (kbd "C-c C-t") #'python-vterm-repl-copy-mode)
(define-key map (kbd "C-c C-z") #'py-vterm-interaction-repl-switch-to-script-buffer)
(define-key map (kbd "M-k") #'py-vterm-interaction-repl-clear-buffer)
(define-key map (kbd "C-c C-t") #'py-vterm-interaction-repl-copy-mode)
(define-key map (kbd "C-l") #'recenter-top-bottom)
(define-key map (kbd "C-c M-r") #'python-vterm-repl-restart)
(define-key map (kbd "C-c M-r") #'py-vterm-interaction-repl-restart)
map))
(define-derived-mode python-vterm-repl-mode vterm-mode "Inf-Python"
(define-derived-mode py-vterm-interaction-repl-mode vterm-mode "Inf-Python"
"A major mode for inferior Python REPL."
:group 'python-vterm-repl)
:group 'py-vterm-interaction-repl)
(defcustom python-vterm-repl-mode-hook nil
(defcustom py-vterm-interaction-repl-mode-hook nil
"Hook run after starting a Python script buffer with an inferior Python REPL."
:type 'hook
:group 'python-vterm-repl)
:group 'py-vterm-interaction-repl)
(defcustom python-vterm-repl-launch-timeout 5
(defcustom py-vterm-interaction-repl-launch-timeout 5
"Time in seconds to wait for the Python REPL to start."
:type 'number
:group 'python-vterm-repl)
:group 'py-vterm-interaction-repl)
(defcustom python-vterm-repl-script-timeout 5
(defcustom py-vterm-interaction-repl-script-timeout 5
"Time in seconds to wait for the Python REPL to execute a script."
:type 'number
:group 'python-vterm-repl)
:group 'py-vterm-interaction-repl)
(defun python-vterm-repl-buffer-name (&optional session-name)
(defun py-vterm-interaction-repl-buffer-name (&optional session-name)
"Return a Python REPL buffer name whose session name is SESSION-NAME.
If SESSION-NAME is not given, the default session name `main' is assumed."
(format "*python:%s*" (or session-name "main")))
(defun python-vterm-repl-session-name (repl-buffer)
(defun py-vterm-interaction-repl-session-name (repl-buffer)
"Return the session name of REPL-BUFFER."
(let ((bn (buffer-name repl-buffer)))
(if (string= (substring bn 1 8) "python:")
(substring bn 8 -1)
nil)))
(defvar python-vterm-repl--launch-timers '()
(defvar py-vterm-interaction-repl--launch-timers '()
"A timer that is used to determine the interpreter upon launch.")
(defun python-vterm--launch (ses-name env context)
(defun py-vterm-interaction--launch (ses-name env context)
"Launch a new Python REPL buffer with SES-NAME and ENV.
If CONTEXT is given, it is used to set the working directory and
the script buffer. It is also attempted to detect whether the
python interpreter is ipython. This times out after
`python-vterm-repl-launch-timeout' seconds."
`py-vterm-interaction-repl-launch-timeout' seconds."
(let ((new-buffer
(generate-new-buffer (python-vterm-repl-buffer-name ses-name)))
(vterm-shell python-vterm-repl-program)
(generate-new-buffer (py-vterm-interaction-repl-buffer-name ses-name)))
(vterm-shell py-vterm-interaction-repl-program)
(vterm-environment (if context (plist-get context :env) env))
(id (gensym "python-vterm-buffer")))
(id (gensym "py-vterm-interaction-buffer")))
(with-current-buffer new-buffer
(when context
(setq default-directory (plist-get context :cwd))
(setq python-vterm-repl-script-buffer (plist-get context :script-buffer)))
(python-vterm-repl-mode)
(setq py-vterm-interaction-repl-script-buffer (plist-get context :script-buffer)))
(py-vterm-interaction-repl-mode)
(push (cons id
(run-with-timer .1 1
(lambda (buffer)
(let ((timer (alist-get id python-vterm-repl--launch-timers)))
(let ((timer (alist-get id py-vterm-interaction-repl--launch-timers)))
(if (and buffer (buffer-live-p buffer))
(if (python-vterm-repl-prompt-status)
(if (py-vterm-interaction-repl-prompt-status)
(progn
(setq python-vterm-repl-interpreter
(if (eq (python-vterm--execute-script "is_ipython") :false)
(setq py-vterm-interaction-repl-interpreter
(if (eq (py-vterm-interaction--execute-script "is_ipython") :false)
:python :ipython))
(cancel-timer timer)))
(cancel-timer timer))))
new-buffer))
python-vterm-repl--launch-timers)
py-vterm-interaction-repl--launch-timers)
(add-function :filter-args (process-filter vterm--process)
(python-vterm-repl-run-filter-functions-func ses-name))
(setq python-vterm-session ses-name)
(message python-vterm-session))
(py-vterm-interaction-repl-run-filter-functions-func ses-name))
(setq py-vterm-interaction-session ses-name)
(message py-vterm-interaction-session))
new-buffer))
(defun python-vterm-repl-buffer (&optional session-name restart)
(defun py-vterm-interaction-repl-buffer (&optional session-name restart)
"Return an inferior Python REPL buffer of the session name SESSION-NAME.
If there exists no such buffer, one is created and returned.
With non-nil RESTART, the existing buffer will be killed and
recreated."
(let ((ses-name (or session-name python-vterm-session "main"))
(let ((ses-name (or session-name py-vterm-interaction-session "main"))
(env (cons "TERM=xterm-256color" process-environment)))
(if-let ((buffer (get-buffer (python-vterm-repl-buffer-name ses-name)))
(if-let ((buffer (get-buffer (py-vterm-interaction-repl-buffer-name ses-name)))
(alive (vterm-check-proc buffer))
(no-restart (not restart)))
buffer
(if (not buffer)
(python-vterm--launch ses-name env nil)
(py-vterm-interaction--launch ses-name env nil)
(save-excursion
(let* ((win (get-buffer-window buffer))
(proc (get-buffer-process buffer))
(context (if proc (python-vterm-repl-get-context buffer))))
(context (if proc (py-vterm-interaction-repl-get-context buffer))))
(with-current-buffer buffer
(rename-buffer (concat (buffer-name) ":orphaned")))
(let ((new-buffer (python-vterm--launch ses-name env context)))
(let ((new-buffer (py-vterm-interaction--launch ses-name env context)))
(when win
(select-window win)
(switch-to-buffer new-buffer))
(if (process-live-p proc) (delete-process proc))
new-buffer)))))))
(defun python-vterm-repl-list-sessions ()
(defun py-vterm-interaction-repl-list-sessions ()
"Return a list of existing Python REPL sessions."
(mapcan (lambda (bn)
(if (string-match "\\*python:\\(.*\\)\\*" bn)
@ -202,7 +202,7 @@ recreated."
nil))
(mapcar #'buffer-name (buffer-list))))
(defun python-vterm-repl (&optional arg)
(defun py-vterm-interaction-repl (&optional arg)
"Create an inferior Python REPL buffer and open it.
The buffer name will be `*python:main*' where `main' is the default session name.
With prefix ARG, prompt for a session name.
@ -210,55 +210,55 @@ If there's already an alive REPL buffer for the session, it will be opened."
(interactive "P")
(let* ((session-name
(cond ((null arg) nil)
(t (completing-read "Session name: " (python-vterm-repl-list-sessions) nil nil nil nil
(python-vterm-repl-session-name (python-vterm-fellow-repl-buffer))))))
(t (completing-read "Session name: " (py-vterm-interaction-repl-list-sessions) nil nil nil nil
(py-vterm-interaction-repl-session-name (py-vterm-interaction-fellow-repl-buffer))))))
(orig-buffer (current-buffer))
(repl-buffer (python-vterm-repl-buffer session-name)))
(if (and (boundp 'python-vterm-mode) python-vterm-mode)
(repl-buffer (py-vterm-interaction-repl-buffer session-name)))
(if (and (boundp 'py-vterm-interaction-mode) py-vterm-interaction-mode)
(with-current-buffer repl-buffer
(setq python-vterm-repl-script-buffer orig-buffer)))
(setq py-vterm-interaction-repl-script-buffer orig-buffer)))
(pop-to-buffer-same-window repl-buffer)))
(defun python-vterm-repl-switch-to-script-buffer ()
(defun py-vterm-interaction-repl-switch-to-script-buffer ()
"Switch to the script buffer that is paired with this Python REPL buffer."
(interactive)
(let ((repl-buffer (current-buffer))
(script-buffer (if (buffer-live-p python-vterm-repl-script-buffer)
python-vterm-repl-script-buffer
(script-buffer (if (buffer-live-p py-vterm-interaction-repl-script-buffer)
py-vterm-interaction-repl-script-buffer
nil)))
(if script-buffer
(with-current-buffer script-buffer
(setq python-vterm-fellow-repl-buffer repl-buffer)
(setq py-vterm-interaction-fellow-repl-buffer repl-buffer)
(switch-to-buffer-other-window script-buffer)))))
(defun python-vterm-repl-restart ()
(defun py-vterm-interaction-repl-restart ()
"Restart the inferior Python process in the current REPL buffer."
(interactive)
(if (y-or-n-p "Restart Python REPL? ")
(python-vterm-repl-buffer (python-vterm-repl-session-name (current-buffer)) t)))
(py-vterm-interaction-repl-buffer (py-vterm-interaction-repl-session-name (current-buffer)) t)))
(defun python-vterm-repl-clear-buffer ()
(defun py-vterm-interaction-repl-clear-buffer ()
"Clear the content of the Python REPL buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(vterm-clear 1)))
(defvar-local python-vterm-repl-filter-functions '()
(defvar-local py-vterm-interaction-repl-filter-functions '()
"List of filter functions that process the output to the REPL buffer.")
(defun python-vterm-repl-run-filter-functions-func (session)
(defun py-vterm-interaction-repl-run-filter-functions-func (session)
"Return a function that runs registered filter functions for SESSION with args."
(lambda (args)
(with-current-buffer (python-vterm-repl-buffer session)
(with-current-buffer (py-vterm-interaction-repl-buffer session)
(let ((proc (car args))
(str (cadr args)))
(let ((funcs python-vterm-repl-filter-functions))
(let ((funcs py-vterm-interaction-repl-filter-functions))
(while funcs
(setq str (apply (pop funcs) (list str))))
(list proc str))))))
(defun python-vterm-repl-prompt-status ()
(defun py-vterm-interaction-repl-prompt-status ()
"Check and return the prompt status of the REPL.
Return a corresponding symbol or nil if not ready for input."
(let* ((bs (buffer-string))
@ -276,15 +276,15 @@ Return a corresponding symbol or nil if not ready for input."
((rx bol "In [" (one-or-more (any "0-9")) "]: " eol) :ipython)))))
(defun python-vterm--get-script-file (name)
(defun py-vterm-interaction--get-script-file (name)
"Return the full path of the script file with NAME."
(expand-file-name (concat "./scripts/" name ".py")
(file-name-directory (symbol-file 'python-vterm-mode))))
(file-name-directory (symbol-file 'py-vterm-interaction-mode))))
(defun python-vterm--execute-script (name &rest args)
(defun py-vterm-interaction--execute-script (name &rest args)
"Load the script with file NAME and call the eponymous function with ARGS.
Returns either the result of the function or nil if execution
times out after `python-vterm-repl-script-timeout'.
times out after `py-vterm-interaction-repl-script-timeout'.
The script file is expected to be in the `scripts' directory of
the package and must contain exactly one function with the same
@ -294,11 +294,11 @@ arguments ARGS and the result is returned as a parsed JSON object
in the plist format. The functions from the utility script are
loaded into the repl as well. All loaded functions and modules
will be cleaned up afterwards."
(let ((utility-file (python-vterm--get-script-file name))
(script-file (python-vterm--get-script-file "utility"))
(tmpfile (make-temp-file "python-vterm--" nil ".json"))
(python-vterm-paste-with-return nil)
(python-vterm-paste-with-clear nil)
(let ((utility-file (py-vterm-interaction--get-script-file name))
(script-file (py-vterm-interaction--get-script-file "utility"))
(tmpfile (make-temp-file "py-vterm-interaction--" nil ".json"))
(py-vterm-interaction-paste-with-return nil)
(py-vterm-interaction-paste-with-clear nil)
(arglist (concat
(seq-reduce
(lambda (el rest)
@ -308,18 +308,18 @@ will be cleaned up afterwards."
(up-to-now (buffer-string))
(result nil))
(python-vterm-clear-line)
(python-vterm-paste-string (format "exec(open(\"%s\").read());" utility-file))
(python-vterm-paste-string (format "exec(open(\"%s\").read());" script-file))
(python-vterm-paste-string (format "%s(\"%s\"%s);" name tmpfile arglist))
(py-vterm-interaction-clear-line)
(py-vterm-interaction-paste-string (format "exec(open(\"%s\").read());" utility-file))
(py-vterm-interaction-paste-string (format "exec(open(\"%s\").read());" script-file))
(py-vterm-interaction-paste-string (format "%s(\"%s\"%s);" name tmpfile arglist))
;; clean up all utility stuff
(python-vterm-paste-string "del dump_json;")
(python-vterm-paste-string (format "del %s" name))
(python-vterm-send-return-key)
(py-vterm-interaction-paste-string "del dump_json;")
(py-vterm-interaction-paste-string (format "del %s" name))
(py-vterm-interaction-send-return-key)
(with-timeout (python-vterm-repl-script-timeout
(progn (display-warning 'python-vterm "Python script did not finish in time.")
(with-timeout (py-vterm-interaction-repl-script-timeout
(progn (display-warning 'py-vterm-interaction "Python script did not finish in time.")
(delete-file tmpfile)
nil))
(while
@ -335,121 +335,121 @@ will be cleaned up afterwards."
(setq result (json-parse-buffer :object-type 'plist :array-type 'list))
nil)))))
(let ((inhibit-read-only t))
(python-vterm-repl-clear-buffer)
(py-vterm-interaction-repl-clear-buffer)
(insert up-to-now))
result)))
(defun python-vterm--read-script (name)
(defun py-vterm-interaction--read-script (name)
"Read the content of the script file with NAME.
The path of the script is expanded relative to the `scripts' directory."
(let ((script-file (python-vterm--get-script-file name)))
(let ((script-file (py-vterm-interaction--get-script-file name)))
(with-temp-buffer
(insert-file-contents script-file)
(buffer-string))))
(defun python-vterm-repl-get-context (buf)
(defun py-vterm-interaction-repl-get-context (buf)
"Obtain context information of the REPL buffer BUF.
This returns a list of the current working directory of teh
inferior Python process and the current active environment."
(with-current-buffer buf
(let ((context (python-vterm--execute-script "get_env")))
(plist-put context :script-buffer python-vterm-repl-script-buffer))))
(let ((context (py-vterm-interaction--execute-script "get_env")))
(plist-put context :script-buffer py-vterm-interaction-repl-script-buffer))))
(defvar python-vterm-repl-copy-mode-map
(defvar py-vterm-interaction-repl-copy-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-t") #'python-vterm-repl-copy-mode)
(define-key map [return] #'python-vterm-repl-copy-mode-done)
(define-key map (kbd "RET") #'python-vterm-repl-copy-mode-done)
(define-key map (kbd "C-c C-t") #'py-vterm-interaction-repl-copy-mode)
(define-key map [return] #'py-vterm-interaction-repl-copy-mode-done)
(define-key map (kbd "RET") #'py-vterm-interaction-repl-copy-mode-done)
(define-key map (kbd "C-c C-r") #'vterm-reset-cursor-point)
map))
(define-minor-mode python-vterm-repl-copy-mode
(define-minor-mode py-vterm-interaction-repl-copy-mode
"Toggle copy mode."
:group 'python-vterm-repl
:group 'py-vterm-interaction-repl
:lighter " VTermCopy"
:keymap python-vterm-repl-copy-mode-map
(if python-vterm-repl-copy-mode
:keymap py-vterm-interaction-repl-copy-mode-map
(if py-vterm-interaction-repl-copy-mode
(progn
(message "Start copy mode")
(use-local-map nil)
(vterm-send-stop))
(vterm-reset-cursor-point)
(use-local-map python-vterm-repl-mode-map)
(use-local-map py-vterm-interaction-repl-mode-map)
(vterm-send-start)
(message "End copy mode")))
(defun python-vterm-repl-copy-mode-done ()
(defun py-vterm-interaction-repl-copy-mode-done ()
"Save the active region to the kill ring and exit copy mode."
(interactive)
(if (region-active-p)
(kill-ring-save (region-beginning) (region-end))
(user-error "No active region"))
(python-vterm-repl-copy-mode -1))
(py-vterm-interaction-repl-copy-mode -1))
;;----------------------------------------------------------------------
(defgroup python-vterm nil
(defgroup py-vterm-interaction nil
"A minor mode to interact with an inferior Python REPL."
:group 'python)
(defcustom python-vterm-hook nil
(defcustom py-vterm-interaction-hook nil
"Hook run after starting a Python script buffer with an inferior Python REPL."
:type 'hook
:group 'python-vterm)
:group 'py-vterm-interaction)
(defvar-local python-vterm-paste-with-return t
(defvar-local py-vterm-interaction-paste-with-return t
"Whether to send a return key after pasting a string to the Python REPL.")
(defvar-local python-vterm-paste-with-clear t
(defvar-local py-vterm-interaction-paste-with-clear t
"Whether to clear the line before pasting a string to the Python REPL.")
(defvar-local python-vterm-fellow-repl-buffer nil)
(defvar-local python-vterm-session nil)
(defvar-local python-vterm-context nil)
(defvar-local py-vterm-interaction-fellow-repl-buffer nil)
(defvar-local py-vterm-interaction-session nil)
(defvar-local py-vterm-interaction-context nil)
(defun python-vterm-fellow-repl-buffer (&optional session-name)
(defun py-vterm-interaction-fellow-repl-buffer (&optional session-name)
"Return the paired REPL buffer or the one specified with SESSION-NAME.
If SESSION-NAME is not specified, try to return the currently
associated repl buffer or create a new one with the currently
associated session-name."
(let ((session (or session-name python-vterm-session)))
(let ((session (or session-name py-vterm-interaction-session)))
(if session
(python-vterm-repl-buffer session-name)
(if (buffer-live-p python-vterm-fellow-repl-buffer)
python-vterm-fellow-repl-buffer
(if python-vterm-session
(python-vterm-repl-buffer python-vterm-session)
(python-vterm-repl-buffer))))))
(py-vterm-interaction-repl-buffer session-name)
(if (buffer-live-p py-vterm-interaction-fellow-repl-buffer)
py-vterm-interaction-fellow-repl-buffer
(if py-vterm-interaction-session
(py-vterm-interaction-repl-buffer py-vterm-interaction-session)
(py-vterm-interaction-repl-buffer))))))
(defun python-vterm-switch-to-repl-buffer (&optional arg)
(defun py-vterm-interaction-switch-to-repl-buffer (&optional arg)
"Switch to the paired REPL buffer or to the one with a specified session name.
With prefix ARG, prompt for session name. If a session name is
provided all future commands will use that session."
(interactive "P")
(let* ((session-name
(cond ((null arg) (or python-vterm-session nil))
(t (completing-read "Session name: " (python-vterm-repl-list-sessions) nil nil nil nil
(python-vterm-repl-session-name (python-vterm-fellow-repl-buffer))))))
(cond ((null arg) (or py-vterm-interaction-session nil))
(t (completing-read "Session name: " (py-vterm-interaction-repl-list-sessions) nil nil nil nil
(py-vterm-interaction-repl-session-name (py-vterm-interaction-fellow-repl-buffer))))))
(script-buffer (current-buffer))
(repl-buffer (python-vterm-fellow-repl-buffer session-name)))
(setq python-vterm-fellow-repl-buffer repl-buffer)
(setq python-vterm-session session-name)
(repl-buffer (py-vterm-interaction-fellow-repl-buffer session-name)))
(setq py-vterm-interaction-fellow-repl-buffer repl-buffer)
(setq py-vterm-interaction-session session-name)
(with-current-buffer repl-buffer
(setq python-vterm-repl-script-buffer script-buffer)
(setq py-vterm-interaction-repl-script-buffer script-buffer)
(switch-to-buffer-other-window repl-buffer))))
(defun python-vterm-send-return-key ()
(defun py-vterm-interaction-send-return-key ()
"Send a return key to the Python REPL."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(vterm-send-return)))
(defun python-vterm-send-backspace ()
(defun py-vterm-interaction-send-backspace ()
"Send a backspace key to the Python REPL."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(vterm-send-backspace)))
(defun python-vterm-send-current-line ()
(defun py-vterm-interaction-send-current-line ()
"Send the current line to the Python REPL, and move to the next line.
This sends a newline after the content of the current line even if there's no
newline at the end. A newline is also inserted after the current line of the
@ -462,65 +462,65 @@ script buffer."
(line (string-trim (thing-at-point 'line t))))
(unless (and (zerop clmn) char)
(when (/= 0 clmn)
(python-vterm-paste-string line)
(if (not python-vterm-paste-with-return)
(python-vterm-send-return-key))
(py-vterm-interaction-paste-string line)
(if (not py-vterm-interaction-paste-with-return)
(py-vterm-interaction-send-return-key))
(if (not char)
(newline))))))
(forward-line))
(defun python-vterm-paste-string (string)
(defun py-vterm-interaction-paste-string (string)
"Send STRING to the Python REPL buffer using brackted paste mode."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(goto-char (point-max))
(when python-vterm-paste-with-clear
(python-vterm-clear-line))
(when py-vterm-interaction-paste-with-clear
(py-vterm-interaction-clear-line))
(vterm-send-string string t)
(if python-vterm-paste-with-return
(python-vterm-send-return-key))))
(if py-vterm-interaction-paste-with-return
(py-vterm-interaction-send-return-key))))
(defun python-vterm-clear-line ()
(defun py-vterm-interaction-clear-line ()
"Clear the current line in the Python REPL buffer."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(goto-char (point-max))
(vterm-send-key (kbd "C-a"))
(vterm-send-key (kbd "C-k"))))
(defun python-vterm-ensure-newline (str)
(defun py-vterm-interaction-ensure-newline (str)
"Add a newline at the end of STR if the last character is not a newline."
(concat str (if (string= (substring str -1 nil) "\n") "" "\n")))
(defun python-vterm-send-region-or-current-line ()
(defun py-vterm-interaction-send-region-or-current-line ()
"Send the content of the region if the region is active, or send the current line."
(interactive)
(if (use-region-p)
(let ((str (buffer-substring-no-properties (region-beginning) (region-end))))
(python-vterm-paste-string str)
(py-vterm-interaction-paste-string str)
(deactivate-mark))
(python-vterm-send-current-line)))
(py-vterm-interaction-send-current-line)))
(defun python-vterm--load-file (file &optional comment)
(defun py-vterm-interaction--load-file (file &optional comment)
"Load the content of the file with FILE into the Python REPL buffer."
(if (eq python-vterm-repl-interpreter :ipython)
(if (eq py-vterm-interaction-repl-interpreter :ipython)
(format "%%run -i %s # %s" file (or comment ""))
(format "exec(open(\"%s\").read()) # %s" file comment)))
(defun python-vterm--send-maybe-silent (string &optional comment)
(defun py-vterm-interaction--send-maybe-silent (string &optional comment)
"Send STRING to the Python REPL buffer, possibly using `%run -i' with a temp file."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(if python-vterm-silent-cells
(let ((tmpfile (make-temp-file "python-vterm" nil ".py")))
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(if py-vterm-interaction-silent-cells
(let ((tmpfile (make-temp-file "py-vterm-interaction" nil ".py")))
(with-temp-file tmpfile
(insert string))
(python-vterm-paste-string (python-vterm--load-file tmpfile comment))
(py-vterm-interaction-paste-string (py-vterm-interaction--load-file tmpfile comment))
(run-with-timer 10 nil
(lambda (tmpfile)
(delete-file tmpfile))
tmpfile))
(python-vterm-paste-string string))))
(py-vterm-interaction-paste-string string))))
(defun python-vterm-send-current-cell ()
(defun py-vterm-interaction-send-current-cell ()
"Send the current code \"cell\" to the Python REPL.
Each block is delimited by `# %% <optional name>`.
@ -539,11 +539,11 @@ point, the cell is assumed to end with the buffer."
(end (or (save-excursion (re-search-forward cell-regex (point-max) t))
(point-max)))
(cell-content (buffer-substring-no-properties start end)))
(python-vterm--send-maybe-silent cell-content cell-name)
(when (not python-vterm-paste-with-return)
(python-vterm-send-return-key))))))
(py-vterm-interaction--send-maybe-silent cell-content cell-name)
(when (not py-vterm-interaction-paste-with-return)
(py-vterm-interaction-send-return-key))))))
(defun python-vterm-run-current-function ()
(defun py-vterm-interaction-run-current-function ()
"Send the current function the Python REPL and paste its name, ready to run.
If the function has no arguments, the function call is run immediately."
(interactive)
@ -554,7 +554,7 @@ If the function has no arguments, the function call is run immediately."
(if name-found
(let ((name (match-string 1))
(args (match-string 2))
(python-vterm-paste-with-return nil)
(py-vterm-interaction-paste-with-return nil)
(begin (region-beginning))
(end (region-end)))
@ -568,86 +568,86 @@ If the function has no arguments, the function call is run immediately."
nil))))
(let ((func (buffer-substring-no-properties begin end)))
(python-vterm--send-maybe-silent func name)
(python-vterm-send-return-key))
(py-vterm-interaction--send-maybe-silent func name)
(py-vterm-interaction-send-return-key))
(if (string-empty-p args)
(progn
(python-vterm-paste-string (format "%s()" name))
(python-vterm-send-return-key))
(python-vterm-paste-string (format "%s(" name))))
(py-vterm-interaction-paste-string (format "%s()" name))
(py-vterm-interaction-send-return-key))
(py-vterm-interaction-paste-string (format "%s(" name))))
(message "No function found"))))))
(defun python-vterm-send-buffer ()
(defun py-vterm-interaction-send-buffer ()
"Send the whole content of the script buffer to the Python REPL line by line."
(interactive)
(let ((python-vterm-paste-with-return t)
(python-vterm-paste-with-clear nil))
(let ((py-vterm-interaction-paste-with-return t)
(py-vterm-interaction-paste-with-clear nil))
(python-vterm-paste-string (buffer-string))))
(py-vterm-interaction-paste-string (buffer-string))))
(defun python-vterm-send-run-buffer-file ()
(defun py-vterm-interaction-send-run-buffer-file ()
"Run the current buffer file in the python vterm buffer.
This is equivalent to running `%run -i <buffer-file-name>` in the python vterm buffer."
(interactive)
(let ((file buffer-file-name)
(python-vterm-paste-with-return t))
(with-current-buffer (python-vterm-fellow-repl-buffer)
(python-vterm-paste-string (python-vterm--load-file file "load script buffer")))))
(py-vterm-interaction-paste-with-return t))
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(py-vterm-interaction-paste-string (py-vterm-interaction--load-file file "load script buffer")))))
(defun python-vterm-send-import-buffer-file ()
(defun py-vterm-interaction-send-import-buffer-file ()
"Import the current buffer file like `from <module> import *' in the python repl."
(interactive)
(let ((file buffer-file-name)
(python-vterm-paste-with-return t))
(with-current-buffer (python-vterm-fellow-repl-buffer)
(python-vterm--execute-script "star_import_script" file))))
(py-vterm-interaction-paste-with-return t))
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(py-vterm-interaction--execute-script "star_import_script" file))))
(defun python-vterm-send-cd-to-buffer-directory ()
(defun py-vterm-interaction-send-cd-to-buffer-directory ()
"Change the REPL's working directory to the directory of the buffer file."
(interactive)
(if buffer-file-name
(let ((buffer-directory (file-name-directory buffer-file-name)))
(with-current-buffer (python-vterm-fellow-repl-buffer)
(python-vterm-paste-string (if (eq python-vterm-repl-interpreter :ipython)
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(py-vterm-interaction-paste-string (if (eq py-vterm-interaction-repl-interpreter :ipython)
(format "%%cd %s" buffer-directory)
(format "import os; os.chdir(\"%s\")" buffer-directory)))
(setq default-directory buffer-directory)))
(message "The buffer is not associated with a directory.")))
(defalias 'python-vterm-sync-wd 'python-vterm-send-cd-to-buffer-directory)
(defalias 'py-vterm-interaction-sync-wd 'py-vterm-interaction-send-cd-to-buffer-directory)
(defun python-vterm-fellow-repl-prompt-status ()
(defun py-vterm-interaction-fellow-repl-prompt-status ()
"Return REPL mode or nil if REPL is not ready for input."
(with-current-buffer (python-vterm-fellow-repl-buffer)
(python-vterm-repl-prompt-status)))
(with-current-buffer (py-vterm-interaction-fellow-repl-buffer)
(py-vterm-interaction-repl-prompt-status)))
;;;###autoload
(define-minor-mode python-vterm-mode
(define-minor-mode py-vterm-interaction-mode
"A minor mode for a Python script buffer that interacts with an inferior Python REPL."
:init-value nil
:lighter " PY"
:keymap
`((,(kbd "C-c C-z") . python-vterm-switch-to-repl-buffer)
(,(kbd "C-c C-c") . python-vterm-send-region-or-current-line)
(,(kbd "C-c C-j") . python-vterm-send-current-cell)
(,(kbd "C-c C-f") . python-vterm-run-current-function)
(,(kbd "C-c C-i") . python-vterm-send-import-buffer-file)
(,(kbd "C-c C-b") . python-vterm-send-buffer)
(,(kbd "C-c C-r") . python-vterm-send-run-buffer-file)
(,(kbd "C-c C-d") . python-vterm-send-cd-to-buffer-directory)))
`((,(kbd "C-c C-z") . py-vterm-interaction-switch-to-repl-buffer)
(,(kbd "C-c C-c") . py-vterm-interaction-send-region-or-current-line)
(,(kbd "C-c C-j") . py-vterm-interaction-send-current-cell)
(,(kbd "C-c C-f") . py-vterm-interaction-run-current-function)
(,(kbd "C-c C-i") . py-vterm-interaction-send-import-buffer-file)
(,(kbd "C-c C-b") . py-vterm-interaction-send-buffer)
(,(kbd "C-c C-r") . py-vterm-interaction-send-run-buffer-file)
(,(kbd "C-c C-d") . py-vterm-interaction-send-cd-to-buffer-directory)))
;;----------------------------------------------------------------------
;; Define some utility aliases but not override if the names are already used.
(unless (fboundp 'python)
(defalias 'python 'python-vterm-repl))
(defalias 'python 'py-vterm-interaction-repl))
(unless (boundp 'python-session)
(defvaralias 'python-session 'python-vterm-session))
(defvaralias 'python-session 'py-vterm-interaction-session))
(provide 'python-vterm)
(provide 'py-vterm-interaction)
;;; python-vterm.el ends here
;;; py-vterm-interaction.el ends here

View file

@ -1,5 +1,5 @@
* REPL Scripts
These are scripts intended to be run in the REPL by =python-vterm=. Each
These are scripts intended to be run in the REPL by =py-vterm-interaction=. Each
script (except =utility.py=) should contain only one function of the
same name as the script file. All functions should not alter the
global state and have no observable side effects in the REPL. They can