From mboxrd@z Thu Jan 1 00:00:00 1970 From: Akater Subject: Feature suggestion and code review request: org-babel-cycle-src-block-header Date: Wed, 28 Feb 2018 10:59:12 +0000 Message-ID: <87muztqt1b.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha512; protocol="application/pgp-signature" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:40352) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eqzVj-00045l-GJ for emacs-orgmode@gnu.org; Wed, 28 Feb 2018 06:03:05 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eqzVe-00085P-JY for emacs-orgmode@gnu.org; Wed, 28 Feb 2018 06:02:59 -0500 Received: from mail-lf0-x241.google.com ([2a00:1450:4010:c07::241]:44662) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eqzVe-00084t-6e for emacs-orgmode@gnu.org; Wed, 28 Feb 2018 06:02:54 -0500 Received: by mail-lf0-x241.google.com with SMTP id v9so2847637lfa.11 for ; Wed, 28 Feb 2018 03:02:53 -0800 (PST) Received: from localhost ([176.121.10.52]) by smtp.googlemail.com with ESMTPSA id o77sm308250lja.43.2018.02.28.03.02.47 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 28 Feb 2018 03:02:50 -0800 (PST) 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" To: emacs-orgmode@gnu.org --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable When I have a chance, I enjoy the following development workflow: the code is written in org files and is tangled into conventional source code files more or less regularly. I find that source blocks mostly fall into three categories, numbered here for further reference: =2D examples/test cases/desiderata, like `(my-implemented-or-desired-function x y)' (type 1)=20 =2D drafts, failed attempts at implementations and other snippets better left as is, or as a warning (type 2) =2D working implementations, to be tangled (type 3) Hence I end up using only a handful of header argument strings. An example corresponding to this 3-cases setup is found below. So it would be nice to have a function that cycles between those, much like we can cycle through org TODO sequence now using a standard function, and set up this sequence per Org file. I'm fairly bad at Emacs Lisp, so I'm interested in feedback about my implementation of cycling function. It operates with strings, mostly because I failed to make it work with lists of alists of header arguments as ob-core.el suggests. On the other hand, given that Emacs Lisp is more string-oriented than it is object-oriented, it might not be a really bad idea. So what do you think? How can this implementation be improved? (Sans using rotate and tracking position in a smarter way.) Does it make sense to include this feature in Org mode? Maybe I missed some existing well-estabilished solutions? This is something akin to =E2=80=9Cliterate programming=E2=80=9D; I'm not a fan of this idea---at least the way it is usually presented---but it is somewhat popular a topic. I have some other feature in mind I'd love to see implemented in Org-Babel: convenient export of src blocks of type 1 (see above) into unit tests (as test cases) and into documentation sources (as examples) but this one is heavily target-language dependent and probably deserves its own thread. #+begin_src emacs-lisp (cl-defun next-maybe-cycled (elem list &key (test #'equal)) "Returns the element in `list' next to the first `elem' found. If `elem' = is found at `list''s very tail, returns `list''s car. `next-maybe-cycled' p= rovides no way to distinguish between \"found nil\" and \"found nothing\"." (let ((sublist (cl-member elem list :test test))) (and sublist (if (cdr sublist) (cadr sublist) (car list))))) (defun shrink-whitespace (string) "Transforms all whitespace instances into single spaces. Trims whitespace= at beginning and end. No argument type checking." (cl-reduce (lambda (string rule) (replace-regexp-in-string (car rule) (cdr rule) string)) '(("[[:blank:]]+" . " ") ("^[[:blank:]]*" . "") ("[[:blank:]]*$" . ""= )) :initial-value string)) (defun string-equal-modulo-whitespace (x y) (string-equal (shrink-whitespace x) (shrink-whitespace y))) (defun org-babel-cycle-src-block-header-string (header-strings) "Cycle through given `header-strings' if currently in Org Babel source co= de block. If current src-block header is not found in `header-strings', swi= tch header to the car of `header-strings'. `header-strings' must be a non-empty list of strings. All whitespace in the= m is shrinked. If UNDO-ed, cursor position is not guaranteed to be preserved." (interactive) (cond ((not (and header-strings (listp header-strings))) (error "No Org Babel header strings list found to cycle through. %S fou= nd intstead." header-strings)) ((not (every #'stringp header-strings)) (error "Malformed list of Org Babel header strings: not all elements ar= e strings in %S." header-strings)) (t (let ((initial-position (point))) (org-babel-goto-src-block-head) ;; here we rely on `org-babel-goto-src-block-head' ;; signalling an error if not in source code block (forward-char (length "#+BEGIN_SRC")) (let* ((fallback-position (point)) (we-were-before-replacement-zone (<=3D initial-position fallback-position))) (let ((default-position-to-return-to initial-position) (old-header-string (delete-and-extract-region (point) (line-end-position)))) (unless we-were-before-replacement-zone (incf default-position-to-return-to (- (length old-header-string)))) (let ((new-header-string (concatenate 'string " " (shrink-whitespace (or (next-maybe-cycled old-header-string header-strings :test #'string-equal-modulo-whitespace) (car header-strings)))))) (insert new-header-string) (unless we-were-before-replacement-zone (incf default-position-to-return-to (length new-header-string))) (goto-char (if (<=3D fallback-position default-position-to-return-to (+ fallback-position (length new-header-string))) fallback-position default-position-to-return-to))))))))) ;; example for mailing list ;; Common Lisp assumed! (defun akater/org-babel-cycle-header nil (interactive)=20=20 (org-babel-cycle-src-block-header-string '("lisp :tangle no :results none" ;; type 2 above "lisp :tangle yes :results none" ;; type 3 above "lisp :results type verbatim" ;; type 1 above ))) #+end_src Ideally, I envision something along these lines (some specific choices below don't really make sense): #+begin_src emacs-lisp (defcustom org-babel-standard-header-sequences-alist '((development-setup-1 (lisp (((:tangle . "no")=20 (:results . "none")) ((:tangle . "yes")=20 (:results . "none")) ((:results . "type verbatim")))) (python (((:tangle . "no")=20 (:results . "none")) ((:tangle . "yes")=20 (:results . "none")) ((:results . "type output")))) ) (development-setup-2 (C (((:tangle . "no")=20 (:results . "none")) ((:tangle . "yes")=20 (:results . "raw")))) (julia (((:tangle . "no")=20 (:results . "none")) ((:tangle . "yes")=20 (:results . "none"))))))) #+end_src --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIcBAEBCgAGBQJalouFAAoJELK+sWGx7H9E2BYP/2utLKNdNonvC/pyc6S3pEHF JCC6eBEpcrS2kMcCxxIKiQGUkRUX9S4ZNuRoaxGvUG3iWpqSUJtZEOrOxMHC+oCk zYwM0B0cDTfcGPO0EFKRZlzwi+gOjImElvBomfh7/wqWa2m+oQjBWWK4X2pkXyiA 0r27lJlchvFfrQJtyL4mIoSqOkxfw7UFwCda9UzKGxrIxKwiaUV4GkKHuF6cuJ2t 8ABKTu5katExhEKeuWVjthgRF0niRuGsgkWOtqppaqLa4hiJbRsDkATrzNtg56dy G/qaHRwZSS5GFVNwurYBnykI51LbLt2vLnKXEUGTDgQqjtQjQKt3FLJPWpamJkCI DvXq8VV8V0K0ZDMmoKN85+whX1hDgVi/GA36bCLnE5qavpoqY6E5J2PJohLia8dQ ZnAMpJCAJYgA/XI5xEjZLt7FEpb6mghaQyPNBIuUWFIMi93gMNS6Hy4kFiAkgFDa sFVxH9RVtkabZSVR3DtZNJj4c4PGETbKSWc8nen+h7Md2pdSKLzLdy1Wb08qwXSL eJL1DcfLwc+ZiqGSiY8k6rZ8wIuHW8wo21c566egS6bPbxDnM5XIMNzkiNjU7nAB 9iFG0mmzqtc2tv2b/9qKvtByKWsqx4M9aFi1SdV/NlAWqDsoXvg+0DTVk2cJohnf NGqTg+uhAad2AecX1H6d =zxDm -----END PGP SIGNATURE----- --=-=-=--