From 40ee1ac8b99d6eb8dea67666c67334dc953f7552 Mon Sep 17 00:00:00 2001 From: Nathaniel Nicandro Date: Tue, 21 May 2019 16:45:44 -0500 Subject: [PATCH] Add new customizable variable `jupyter-repl-echo-eval-p` Closes #71 --- README.org | 10 +++++++++ jupyter-repl.el | 38 ++++++++++++++++++++++++++++++++++ test/jupyter-test.el | 49 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/README.org b/README.org index 3393389..3cd9192 100644 --- a/README.org +++ b/README.org @@ -298,6 +298,16 @@ If non-nil, allow inserting a newline in a REPL cell whenever the kernel is busy. Normally this isn't allowed since the REPL relies on the kernel responding to messages when =RET= is pressed, but a kernel does not respond to messages when it is busy. +*** =jupyter-repl-echo-eval-p= + +If non-nil, when evaluating code using the =jupyter-eval-*= functions +like =M-x jupyter-eval-line-or-region=, copy the evaluated code as a REPL input +cell and display any output generated in the REPL. When this variable is nil, +copying to the REPL does not occur and output/results are inserted in pop-up +buffers or added to the =*Messages*= buffer according to +=jupyter-eval-short-result-max-lines= and +=jupyter-eval-short-result-display-function=. + *** Widget support There is also support for Jupyter widgets integrated into the REPL. If any of diff --git a/jupyter-repl.el b/jupyter-repl.el index 9a7d4ad..81e9979 100644 --- a/jupyter-repl.el +++ b/jupyter-repl.el @@ -142,6 +142,20 @@ send the code to the kernel." :type 'boolean :group 'jupyter-repl) +(defcustom jupyter-repl-echo-eval-p nil + "Copy evaluation input to a REPL cell if non-nil. +If non-nil, and when calling the `jupyter-eval-*' functions like +`jupyter-eval-line-or-region', copy the input into a REPL cell. +Otherwise the evaluation request is sent to the kernel without +displaying the code of the request in the REPL. + +Note, output generated by requests will always be sent to the +REPL buffer whenever this variable is non-nil. When the REPL +buffer isn't visible, output will also be sent to pop-up buffers +as is done when this variable is nil." + :type 'boolean + :group 'jupyter-repl) + ;;; Implementation (defclass jupyter-repl-client (jupyter-widget-client jupyter-kernel-client) @@ -1409,6 +1423,30 @@ value." (prog1 ex (jupyter-repl-history-add ex))))) +(cl-defmethod jupyter-eval-string (str + &context (jupyter-current-client jupyter-repl-client) + &optional cb) + (jupyter-with-repl-buffer jupyter-current-client + (when jupyter-repl-echo-eval-p + (goto-char (point-max)) + (jupyter-repl-replace-cell-code str) + (setq str nil)) + (let* ((jupyter-inhibit-handlers + ;; When copying the input to the REPL we need the handlers to + ;; update the REPL state + (unless jupyter-repl-echo-eval-p + '(not :input-request))) + (req (jupyter-send-execute-request jupyter-current-client + :code str + :store-history jupyter-repl-echo-eval-p))) + (prog1 req + ;; Add callbacks to display evaluation output in pop-up buffers either + ;; when we aren't copying the input to a REPL cell or, if we are, when + ;; the REPL buffer isn't visible + (unless (and jupyter-repl-echo-eval-p + (get-buffer-window nil 'visible)) + (jupyter-eval-add-callbacks req cb)))))) + ;;; Kernel management (defun jupyter-repl-interrupt-kernel () diff --git a/test/jupyter-test.el b/test/jupyter-test.el index 62f9c4d..b525ab5 100644 --- a/test/jupyter-test.el +++ b/test/jupyter-test.el @@ -1642,6 +1642,55 @@ last element being the newest element added to the history." (let ((kill-buffer-query-functions nil)) (kill-buffer))))))) +(ert-deftest jupyter-repl-echo-eval-p () + :tags '(repl) + (jupyter-test-with-python-repl client + (jupyter-ert-info ("Copying input") + (let ((jupyter-repl-echo-eval-p t)) + (should (equal (jupyter-repl-cell-code) "")) + (let ((req (jupyter-eval-string "1 + 1"))) + (should-not (jupyter-request-inhibited-handlers req)) + (jupyter-wait-until-idle req) + (jupyter-repl-goto-cell req) + (should (equal (jupyter-repl-cell-code) "1 + 1"))))) + (jupyter-ert-info ("Not copying input") + (let ((jupyter-repl-echo-eval-p nil)) + (should (equal (jupyter-repl-cell-code) "")) + (let ((req (jupyter-eval-string "1 + 1"))) + (should (jupyter-request-inhibited-handlers req)) + (jupyter-wait-until-idle req) + (should-error (jupyter-repl-goto-cell req))))) + (ert-info ("Add callbacks when REPL buffer is invisible") + (cl-letf (((symbol-function #'get-buffer-window) + (lambda (&rest _) nil))) + (ert-info ("`jupyter-repl-echo-eval-p' = t") + (let* ((jupyter-repl-echo-eval-p t) + (req (jupyter-eval-string "1 + 1"))) + (should-not (jupyter-request-inhibited-handlers req)) + (should (jupyter-request-callbacks req)) + (jupyter-wait-until-idle req))) + (ert-info ("`jupyter-repl-echo-eval-p' = nil") + (let* ((jupyter-repl-echo-eval-p nil) + (req (jupyter-eval-string "1 + 1"))) + (should (jupyter-request-inhibited-handlers req)) + (should (jupyter-request-callbacks req)) + (jupyter-wait-until-idle req))))) + (ert-info ("No callbacks when REPL buffer visible") + (cl-letf (((symbol-function #'get-buffer-window) + (lambda (&rest _) (selected-window)))) + (ert-info ("`jupyter-repl-echo-eval-p' = t") + (let* ((jupyter-repl-echo-eval-p t) + (req (jupyter-eval-string "1 + 1"))) + (should-not (jupyter-request-inhibited-handlers req)) + (should-not (jupyter-request-callbacks req)) + (jupyter-wait-until-idle req))) + (ert-info ("`jupyter-repl-echo-eval-p' = nil") + (let* ((jupyter-repl-echo-eval-p nil) + (req (jupyter-eval-string "1 + 1"))) + (should (jupyter-request-inhibited-handlers req)) + (should (jupyter-request-callbacks req)) + (jupyter-wait-until-idle req))))))) + ;;; `org-mode' (defvar org-babel-jupyter-resource-directory nil)