mirror of
https://github.com/vale981/py-vterm-interaction.el
synced 2025-03-04 17:41:40 -05:00
implement running general scripts and detecting ipython
This commit is contained in:
parent
2d9cc5f0c5
commit
cc4ae4f19b
4 changed files with 100 additions and 37 deletions
113
python-vterm.el
113
python-vterm.el
|
@ -77,6 +77,10 @@ parameters may be used, like python -q")
|
||||||
"If non-nil, the PYTHON-VTERM-SEND-CURRENT-CELL will use ipythons `%run` magic to run a code cell.")
|
"If non-nil, the PYTHON-VTERM-SEND-CURRENT-CELL will use ipythons `%run` magic to run a code cell.")
|
||||||
|
|
||||||
(defvar-local python-vterm-repl-script-buffer nil)
|
(defvar-local python-vterm-repl-script-buffer nil)
|
||||||
|
(defvar-local python-vterm-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 python-vterm-repl-mode-map
|
||||||
(let ((map (make-sparse-keymap)))
|
(let ((map (make-sparse-keymap)))
|
||||||
|
@ -110,6 +114,9 @@ If SESSION-NAME is not given, the default session name `main' is assumed."
|
||||||
|
|
||||||
|
|
||||||
(defun python-vterm--launch (ses-name env context)
|
(defun python-vterm--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."
|
||||||
(let ((new-buffer
|
(let ((new-buffer
|
||||||
(generate-new-buffer (python-vterm-repl-buffer-name ses-name)))
|
(generate-new-buffer (python-vterm-repl-buffer-name ses-name)))
|
||||||
(vterm-shell python-vterm-repl-program)
|
(vterm-shell python-vterm-repl-program)
|
||||||
|
@ -119,6 +126,16 @@ If SESSION-NAME is not given, the default session name `main' is assumed."
|
||||||
(setq default-directory (plist-get context :cwd))
|
(setq default-directory (plist-get context :cwd))
|
||||||
(setq python-vterm-repl-script-buffer (plist-get context :script-buffer)))
|
(setq python-vterm-repl-script-buffer (plist-get context :script-buffer)))
|
||||||
(python-vterm-repl-mode)
|
(python-vterm-repl-mode)
|
||||||
|
(run-with-timer .5 nil
|
||||||
|
(lambda (buffer)
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(while (not (python-vterm-repl-prompt-status))
|
||||||
|
(sit-for 0.1))
|
||||||
|
(setq python-vterm-repl-interpreter
|
||||||
|
(if (python-vterm--execute-script "is_ipython")
|
||||||
|
:ipython :python))))
|
||||||
|
new-buffer)
|
||||||
|
|
||||||
(add-function :filter-args (process-filter vterm--process)
|
(add-function :filter-args (process-filter vterm--process)
|
||||||
(python-vterm-repl-run-filter-functions-func ses-name)))
|
(python-vterm-repl-run-filter-functions-func ses-name)))
|
||||||
new-buffer))
|
new-buffer))
|
||||||
|
@ -233,14 +250,61 @@ Return a corresponding symbol or nil if not ready for input."
|
||||||
|
|
||||||
(defun python-vterm--get-script-file (name)
|
(defun python-vterm--get-script-file (name)
|
||||||
"Return the full path of the script file with NAME."
|
"Return the full path of the script file with NAME."
|
||||||
(expand-file-name (concat "./scripts/" name)
|
(expand-file-name (concat "./scripts/" name ".py")
|
||||||
(file-name-directory (symbol-file 'python-vterm-mode))))
|
(file-name-directory (symbol-file 'python-vterm-mode))))
|
||||||
|
|
||||||
(defun python-vterm--execute-script (name)
|
(defun python-vterm--execute-script (name &rest args)
|
||||||
"Execute the python script file with NAME."
|
"Load the script with file NAME and call the eponymous function with ARGS.
|
||||||
(let ((script-file (python-vterm--get-script-file name)))
|
|
||||||
(python-vterm-paste-string (format "exec(open(\"%s\").read())" script-file))
|
The script file is expected to be in the `scripts' directory of
|
||||||
(if (not python-vterm-paste-with-return) (vterm-send-return))))
|
the package and must contain exactly one function with the same
|
||||||
|
name as the file without the extension. The function must return
|
||||||
|
a JSON-serializable object. The function is called with the
|
||||||
|
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)
|
||||||
|
(arglist (concat
|
||||||
|
(seq-reduce
|
||||||
|
(lambda (el rest)
|
||||||
|
(format "%s, \"%s" el rest))
|
||||||
|
args "")
|
||||||
|
(if (seq-empty-p args) "" "\"")))
|
||||||
|
(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))
|
||||||
|
|
||||||
|
;; 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)
|
||||||
|
|
||||||
|
(while
|
||||||
|
(progn (with-temp-buffer
|
||||||
|
(insert-file-contents tmpfile)
|
||||||
|
(buffer-string)
|
||||||
|
(if (= (buffer-size) 0)
|
||||||
|
(progn
|
||||||
|
(sleep-for 0.1)
|
||||||
|
t)
|
||||||
|
(progn
|
||||||
|
(delete-file tmpfile)
|
||||||
|
(setq result (json-parse-buffer :object-type 'plist :array-type 'list))
|
||||||
|
nil)))))
|
||||||
|
(let ((inhibit-read-only t))
|
||||||
|
(python-vterm-repl-clear-buffer)
|
||||||
|
(insert up-to-now))
|
||||||
|
result))
|
||||||
|
|
||||||
|
|
||||||
(defun python-vterm--read-script (name)
|
(defun python-vterm--read-script (name)
|
||||||
"Read the content of the script file with NAME.
|
"Read the content of the script file with NAME.
|
||||||
|
@ -255,27 +319,8 @@ The path of the script is expanded relative to the `scripts' directory."
|
||||||
This returns a list of the current working directory of teh
|
This returns a list of the current working directory of teh
|
||||||
inferior Python process and the current active environment."
|
inferior Python process and the current active environment."
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(let ((tmpfile (make-temp-file "python-vterm-context-" nil ".json")))
|
(let ((context (python-vterm--execute-script "get_env")))
|
||||||
(let ((uid (concat "python-vterm:" (org-id-uuid))))
|
(plist-put context :script-buffer python-vterm-repl-script-buffer))))
|
||||||
(python-vterm--execute-script "get_env.py")
|
|
||||||
(python-vterm-paste-string (concat "python_vterm__get_context(\"" tmpfile "\")\n"))
|
|
||||||
(if (not python-vterm-paste-with-return) (vterm-send-return))
|
|
||||||
(let ((context-json ""))
|
|
||||||
(while
|
|
||||||
(progn (setq context-json
|
|
||||||
(with-temp-buffer
|
|
||||||
(insert-file-contents tmpfile)
|
|
||||||
(buffer-string)))
|
|
||||||
(if (string= context-json "")
|
|
||||||
(progn (sleep-for 0.1)
|
|
||||||
t)
|
|
||||||
nil)))
|
|
||||||
(delete-file tmpfile)
|
|
||||||
(let ((context
|
|
||||||
(json-parse-string (string-replace "\n" "" context-json)
|
|
||||||
:object-type 'plist
|
|
||||||
:array-type 'list)))
|
|
||||||
(plist-put context :script-buffer python-vterm-repl-script-buffer)))))))
|
|
||||||
|
|
||||||
(defvar python-vterm-repl-copy-mode-map
|
(defvar python-vterm-repl-copy-mode-map
|
||||||
(let ((map (make-sparse-keymap)))
|
(let ((map (make-sparse-keymap)))
|
||||||
|
@ -322,6 +367,9 @@ inferior Python process and the current active environment."
|
||||||
(defvar-local python-vterm-paste-with-return t
|
(defvar-local python-vterm-paste-with-return t
|
||||||
"Whether to send a return key after pasting a string to the Python REPL.")
|
"Whether to send a return key after pasting a string to the Python REPL.")
|
||||||
|
|
||||||
|
(defvar-local python-vterm-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-fellow-repl-buffer nil)
|
||||||
(defvar-local python-vterm-session nil)
|
(defvar-local python-vterm-session nil)
|
||||||
(defvar-local python-vterm-context nil)
|
(defvar-local python-vterm-context nil)
|
||||||
|
@ -387,12 +435,19 @@ If SESSION-NAME is given, the REPL with the session name, otherwise
|
||||||
the main REPL, is used."
|
the main REPL, is used."
|
||||||
(with-current-buffer (python-vterm-fellow-repl-buffer session-name)
|
(with-current-buffer (python-vterm-fellow-repl-buffer session-name)
|
||||||
(goto-char (point-max))
|
(goto-char (point-max))
|
||||||
(vterm-send-key (kbd "C-a"))
|
(when python-vterm-paste-with-clear
|
||||||
(vterm-send-key (kbd "C-k"))
|
(python-vterm-clear-line session-name))
|
||||||
(vterm-send-string string t)
|
(vterm-send-string string t)
|
||||||
(if python-vterm-paste-with-return
|
(if python-vterm-paste-with-return
|
||||||
(python-vterm-send-return-key))))
|
(python-vterm-send-return-key))))
|
||||||
|
|
||||||
|
(defun python-vterm-clear-line (&optional session-name)
|
||||||
|
"Clear the current line in the Python REPL buffer."
|
||||||
|
(with-current-buffer (python-vterm-fellow-repl-buffer session-name)
|
||||||
|
(goto-char (point-max))
|
||||||
|
(vterm-send-key (kbd "C-a"))
|
||||||
|
(vterm-send-key (kbd "C-k"))))
|
||||||
|
|
||||||
(defun python-vterm-ensure-newline (str)
|
(defun python-vterm-ensure-newline (str)
|
||||||
"Add a newline at the end of STR if the last character is not a newline."
|
"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")))
|
(concat str (if (string= (substring str -1 nil) "\n") "" "\n")))
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
def python_vterm__get_context(tempfile):
|
def get_env(tempfile):
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
with open(tempfile, "w") as f:
|
dump_json(
|
||||||
context = dict(
|
tempfile,
|
||||||
|
dict(
|
||||||
env=["=".join(el) for el in list(os.environ.items())],
|
env=["=".join(el) for el in list(os.environ.items())],
|
||||||
cwd=os.getcwd(),
|
cwd=os.getcwd(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
json.dump(context, f)
|
|
||||||
|
|
||||||
del os, json
|
|
||||||
|
|
5
scripts/is_ipython.py
Normal file
5
scripts/is_ipython.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def is_ipython(tempfile):
|
||||||
|
try:
|
||||||
|
return dump_json(tempfile, __IPYTHON__)
|
||||||
|
except NameError:
|
||||||
|
return dump_json(tempfile, False)
|
5
scripts/utility.py
Normal file
5
scripts/utility.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def dump_json(tempfile, value):
|
||||||
|
import json
|
||||||
|
|
||||||
|
with open(tempfile, "w") as f:
|
||||||
|
json.dump(value, f)
|
Loading…
Add table
Reference in a new issue