From mboxrd@z Thu Jan 1 00:00:00 1970 From: Frederick Giasson Subject: [PATCH] new :async feature for org-babel-clojure Date: Wed, 6 Apr 2016 12:27:37 -0400 Message-ID: <570538F9.8030307@fgiasson.com> References: <56FABD8E.2000705@fgiasson.com> <87twjfcbt6.fsf@nicolasgoaziou.fr> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------000008010704020005050704" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:53421) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1anqIh-0001VS-QI for emacs-orgmode@gnu.org; Wed, 06 Apr 2016 12:27:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1anqIb-000674-TZ for emacs-orgmode@gnu.org; Wed, 06 Apr 2016 12:27:27 -0400 Received: from s052.panelboxmanager.com ([72.55.186.33]:55671) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1anqIb-00066x-Lo for emacs-orgmode@gnu.org; Wed, 06 Apr 2016 12:27:21 -0400 Received: from mailnull by s052.panelboxmanager.com with sa-checked (Exim 4.86_1) (envelope-from ) id 1anqIZ-000LAT-Sh for emacs-orgmode@gnu.org; Wed, 06 Apr 2016 12:27:20 -0400 Received: from modemcable161.56-23-96.mc.videotron.ca ([96.23.56.161]:57068 helo=[192.168.0.13]) by s052.panelboxmanager.com with esmtpsa (TLSv1.2:DHE-RSA-AES128-SHA:128) (Exim 4.86_1) (envelope-from ) id 1anqIZ-000LAD-LI for emacs-orgmode@gnu.org; Wed, 06 Apr 2016 12:27:19 -0400 In-Reply-To: <87twjfcbt6.fsf@nicolasgoaziou.fr> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org This is a multi-part message in MIME format. --------------000008010704020005050704 Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit Hi, Here is my proposal to create a new :async feature for Org-babel-clojure. This is discussed at length in this blog post: http://fgiasson.com/blog/index.php/2016/04/05/using-clojure-in-org-mode-and-implementing-asynchronous-processing/ I added the commit of the changes, the commit for the ORG-NEWS file and the commit for the Worg documentation. IMPORTANT NOTE: this includes the patches for the new :async feature *AND* the new org-babel-clojure-sync-nrepl-timeout setting (for consistency's sake) Thanks, Fred --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0003-Adding-information-about-the-new-org-babel-clojure-a.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0003-Adding-information-about-the-new-org-babel-clojure-a.pa"; filename*1="tch" >From 2fe515d1fb89ba1ea160711d10f2f68fe0287246 Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 12:18:21 -0400 Subject: [PATCH 3/4] Adding information about the new org-babel-clojure :async feature. --- etc/ORG-NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index e684587..fad0679 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -110,6 +110,15 @@ becomes This new option tells the =org-babel-clojure= module the length of the timeout of a synchronous call to the nREPL. This value is in seconds. If =nil= then no timeout will occur. +*** New code block option =:async= for =org-babel-clojure= +This new option gives the possibility to =org-babel-clojure= user to run +a block code asynchronously using a new code block parameter =:async=. +If =:async= is specified in the code block, then it will be processed +asynchronously by Org-mode. This means that everything that will be +output to the REPL by the code will appear in a new window when it will +be output. Once the processing is finalized, then the window will be closed +and the output will be added to the results section (if =:results output= is +specified). *** New org-protocol key=value syntax Org-protocol can now handle query-style parameters such as: -- 1.9.5.msysgit.0 --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0004-Adding-a-new-feature-async-feature-to-org-babel-cloj.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0004-Adding-a-new-feature-async-feature-to-org-babel-cloj.pa"; filename*1="tch" >From e218aaef38f6dc0f9affa516aafa18b77c2a3a1e Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 12:19:11 -0400 Subject: [PATCH 4/4] Adding a new feature :async feature to org-babel-clojure. With this new feature, someone can evaluate Clojure code in Org-mode code blocks asynchronously. --- lisp/ob-clojure.el | 78 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/lisp/ob-clojure.el b/lisp/ob-clojure.el index 86f1cff..6059d7d 100644 --- a/lisp/ob-clojure.el +++ b/lisp/ob-clojure.el @@ -91,35 +91,79 @@ body))) (defun org-babel-execute:clojure (body params) - "Execute a block of Clojure code with Babel." + "Execute a block of Clojure code with Babel. The block can be executed + synchenously by default or asynchronously with the :async parameter" (let ((expanded (org-babel-expand-body:clojure body params)) + (sbuffer "*Clojure Sub Buffer*") + (async (if (assoc :async params) t nil)) + (response (cons 'dict nil)) + status result) (case org-babel-clojure-backend (cider (require 'cider) (let ((result-params (cdr (assoc :result-params params)))) - (setq result - (nrepl-dict-get - (let ((nrepl-sync-request-timeout org-babel-clojure-sync-nrepl-timeout)) - (nrepl-sync-request:eval - expanded (cider-current-connection) (cider-current-session))) - (if (or (member "output" result-params) - (member "pp" result-params)) - "out" - "value"))))) + ; Check if the user want to run code asynchronously + (when async + ; Create a new window with the async output buffer + (switch-to-buffer-other-window sbuffer) + + ; Run the Clojure code asynchronously in nREPL + (nrepl-request:eval + expanded + (lambda (resp) + (when (member "out" resp) + ; Print the output of the nREPL in the asyn output buffer + (princ (nrepl-dict-get resp "out") (get-buffer sbuffer))) + (nrepl--merge response resp) + ; Update the status of the nREPL output session + (setq status (nrepl-dict-get response "status"))) + (cider-current-connection) + (cider-current-session)) + + ; Wait until the nREPL code finished to be processed + (while (not (member "done" status)) + (nrepl-dict-put response "status" (remove "need-input" status)) + (accept-process-output nil 0.01) + (redisplay)) + + ; Delete the async buffer & window when the processing is finalized + (let ((wins (get-buffer-window-list sbuffer nil t))) + (dolist (win wins) + (delete-window win)) + (kill-buffer sbuffer)) + + ; Put the output or the value in the result section of the code block + (setq result (nrepl-dict-get response + (if (or (member "output" result-params) + (member "pp" result-params)) + "out" + "value")))) + ; Check if user want to run code synchronously + (when (not async) + (setq result + (nrepl-dict-get + (let ((nrepl-sync-request-timeout + org-babel-clojure-sync-nrepl-timeout)) + (nrepl-sync-request:eval + expanded (cider-current-connection) (cider-current-session))) + (if (or (member "output" result-params) + (member "pp" result-params)) + "out" + "value")))))) (slime (require 'slime) (with-temp-buffer - (insert expanded) - (setq result - (slime-eval - `(swank:eval-and-grab-output - ,(buffer-substring-no-properties (point-min) (point-max))) - (cdr (assoc :package params))))))) + (insert expanded) + (setq result + (slime-eval + `(swank:eval-and-grab-output + ,(buffer-substring-no-properties (point-min) (point-max))) + (cdr (assoc :package params))))))) (org-babel-result-cond (cdr (assoc :result-params params)) result (condition-case nil (org-babel-script-escape result) - (error result))))) + (error result))))) (provide 'ob-clojure) -- 1.9.5.msysgit.0 --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0001-Update-of-ORG-NEWS-to-mention-the-addition-of-the-ne.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Update-of-ORG-NEWS-to-mention-the-addition-of-the-ne.pa"; filename*1="tch" >From 9a81363719f3078d13de9b971440ee6fd456b4b2 Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 10:24:54 -0400 Subject: [PATCH 1/4] Update of ORG-NEWS to mention the addition of the new setting "org-babel-clojure-sync-nrepl-timeout" --- etc/ORG-NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 82d5ad0..e684587 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -106,6 +106,10 @@ becomes : ("pdf" . (lambda (file link) (foo))) ** New features +*** New option =org-babel-clojure-sync-nrepl-timeout= for =org-babel-clojure= +This new option tells the =org-babel-clojure= module the length of the timeout +of a synchronous call to the nREPL. This value is in seconds. If =nil= then +no timeout will occur. *** New org-protocol key=value syntax Org-protocol can now handle query-style parameters such as: -- 1.9.5.msysgit.0 --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0002-Addition-of-a-new-customization-variable-called-org-.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0002-Addition-of-a-new-customization-variable-called-org-.pa"; filename*1="tch" >From dab6472228d1a0a41831dcae969335d0a7fb7531 Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 10:46:35 -0400 Subject: [PATCH 2/4] Addition of a new customization variable called "org-babel-clojure-sync-nrepl-timeout" which expose the Cider timeout setting as a custom variable. The timeout is in second, and if nil is specified and timeout is disabled. With previous version, the timeout was set to 10 seconds, and if you were running a function that was taking more than 10 seconds, then you were getting a nREPL timeout error. This is why it is important to expose that setting to the org-mode users. --- lisp/ob-clojure.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lisp/ob-clojure.el b/lisp/ob-clojure.el index 89a09a9..86f1cff 100644 --- a/lisp/ob-clojure.el +++ b/lisp/ob-clojure.el @@ -56,6 +56,12 @@ (defvar org-babel-default-header-args:clojure '()) (defvar org-babel-header-args:clojure '((package . :any))) +(defcustom org-babel-clojure-sync-nrepl-timeout 10 + "Timeout value, in seconds, of a Clojure sync call. + If the value is nil, timeout is disabled." + :type 'integer + :group 'org-babel) + (defcustom org-babel-clojure-backend (cond ((featurep 'cider) 'cider) (t 'slime)) @@ -94,8 +100,9 @@ (let ((result-params (cdr (assoc :result-params params)))) (setq result (nrepl-dict-get - (nrepl-sync-request:eval - expanded (cider-current-connection) (cider-current-session)) + (let ((nrepl-sync-request-timeout org-babel-clojure-sync-nrepl-timeout)) + (nrepl-sync-request:eval + expanded (cider-current-connection) (cider-current-session))) (if (or (member "output" result-params) (member "pp" result-params)) "out" -- 1.9.5.msysgit.0 --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0001-Document-the-new-option-ob-clojure-option-org-babel-.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Document-the-new-option-ob-clojure-option-org-babel-.pa"; filename*1="tch" >From c8e89d774590e9b39604252b3a344f95b56e3924 Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 10:00:18 -0400 Subject: [PATCH 1/2] Document the new option ob-clojure option "org-babel-clojure-sync-nrepl-timeout". --- org-contrib/babel/languages/ob-doc-clojure.org | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/org-contrib/babel/languages/ob-doc-clojure.org b/org-contrib/babel/languages/ob-doc-clojure.org index 72942df..17c0f20 100644 --- a/org-contrib/babel/languages/ob-doc-clojure.org +++ b/org-contrib/babel/languages/ob-doc-clojure.org @@ -90,6 +90,17 @@ Add these lines to your .emacs file to configure CIDER: (require 'cider) #+END_SRC +Optionally you can specify the Cider timeout by setting the =org-babel-clojure-sync-nrepl-timeout= +setting option. The value is in seconds and if set to =nil= then no timeout will occur. + +#+BEGIN_SRC emacs-lisp + ; Disable Cider's timeout + (setq org-babel-clojure-sync-nrepl-timeout nil) + + ; Set the timeout to 20 seconds + (setq org-babel-clojure-sync-nrepl-timeout 20) +#+END_SRC + ** Create a Clojure Project with Leiningen Create a Leiningen project directory tree: -- 1.9.5.msysgit.0 --------------000008010704020005050704 Content-Type: text/plain; charset=UTF-8; name="0002-Documentation-for-the-new-org-babel-clojure-async-fe.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0002-Documentation-for-the-new-org-babel-clojure-async-fe.pa"; filename*1="tch" >From cbe52d3e91d02041513fe3ff149b766b48ad09d8 Mon Sep 17 00:00:00 2001 From: Frederick Giasson Date: Wed, 6 Apr 2016 12:13:21 -0400 Subject: [PATCH 2/2] Documentation for the new org-babel-clojure :async feature. --- org-contrib/babel/languages/ob-doc-clojure.org | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/org-contrib/babel/languages/ob-doc-clojure.org b/org-contrib/babel/languages/ob-doc-clojure.org index 17c0f20..d309975 100644 --- a/org-contrib/babel/languages/ob-doc-clojure.org +++ b/org-contrib/babel/languages/ob-doc-clojure.org @@ -145,6 +145,31 @@ Another source for information on options is this page at Worg: Next, a similar process for executing code will be used with Clojure. +* Using Asynchronous Evaluations +Org-babel-clojure does support asynchronous evaluation of the code blocks. To +evaluate some code asynchronously, you only have to specify the =:async= option +in the code block's header like this: + +#+begin_example +#+BEGIN_SRC clojure :results output :async + (dotimes [n 10] + (println n ".") + (Thread/sleep 500)) +#+END_SRC +#+end_example + +If =:async= is specified that way, then when you will run the code using +=C-c C-c= then a new window will open in Emacs. Everything that is output +by the REPL will immediately be added to that new window. + +When the processing of the code is finished, then the window and its +buffer will be closed and the results will be reported in the +=#+RESULTS= section. Note that the =:results= parameter like normally. If +=silent= is specified, then no result will be displayed. If =output= is +specified then all the output from the window will appears in the results +section. If =value= is specified, then only the last returned value of +the code will be displayed in the results section. + * Connect to the REPL To compile and run Clojure code, you will need to connect to a REPL (Read Evaluation Print Loop). -- 1.9.5.msysgit.0 --------------000008010704020005050704--