From mboxrd@z Thu Jan 1 00:00:00 1970 From: John Kitchin Subject: Re: Feature suggestion and code review request: org-babel-cycle-src-block-header Date: Sun, 4 Mar 2018 20:12:56 -0800 Message-ID: References: <87muztqt1b.fsf@gmail.com> <87d10l9bse.fsf@gmail.com> <878tb8aos1.fsf@gmail.com> <87woyrqsnd.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="001a114989847de7f40566a28ada" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:54836) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eshUl-0001Lv-Ht for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 23:13:07 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eshUh-0007Y1-Fr for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 23:13:03 -0500 Received: from mail-wr0-x231.google.com ([2a00:1450:400c:c0c::231]:35565) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eshUh-0007Xx-02 for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 23:12:59 -0500 Received: by mail-wr0-x231.google.com with SMTP id l43so15809126wrc.2 for ; Sun, 04 Mar 2018 20:12:58 -0800 (PST) In-Reply-To: <87woyrqsnd.fsf@gmail.com> 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: Thorsten Jolitz Cc: org-mode-email --001a114989847de7f40566a28ada Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I guess this is a feature of deleting a region with the point in it. This code, for example, does not preserve point. #+BEGIN_SRC emacs-lisp "<>" (save-excursion (let* ((p1 (point)) (p2 (re-search-backward (concat "<" ">"))) (content (buffer-substring-no-properties p1 p2))) (delete-region p1 p2) (insert content))) #+END_SRC John ----------------------------------- Professor John Kitchin Doherty Hall A207F Department of Chemical Engineering Carnegie Mellon University Pittsburgh, PA 15213 412-268-7803 @johnkitchin http://kitchingroup.cheme.cmu.edu On Sun, Mar 4, 2018 at 4:21 PM, Thorsten Jolitz wrote: > John Kitchin writes: > > > Thanks for the examples. > > > > There is an interesting issue, the following does not save-excursion! > > > > (save-excursion > > (org-dp-rewire 'src-block t t ;cont ins > > t ;aff > > nil ;elem > > :parameters ":results output")) > > > > The point gets moved. Do you know why that happens? > > Hmm ... org-dp-rewire is mostly fidling around with lists, but in the > end it acts conditionally on the 'replace' parameter: > > ,---- > | (if (and (marker-position beg) > | (marker-position end)) > | (cl-case replace > | (append (save-excursion (goto-char end) (insert strg))) > | (prepend (goto-char beg) (insert strg)) > | (t (if (not replace) > | strg > | (delete-region beg end) > | (goto-char end) > | (set-marker beg nil) > | (set-marker paff nil) > | (set-marker end nil) > | (save-excursion (insert strg))))) > | (if replace (insert strg) strg)))) > `---- > > append or prepend result, return it as string, or replace the rewired > element. > I guess the is a save-excursion missing here ... > > > John > > > > ----------------------------------- > > Professor John Kitchin > > Doherty Hall A207F > > Department of Chemical Engineering > > Carnegie Mellon University > > Pittsburgh, PA 15213 > > 412-268-7803 > > @johnkitchin > > http://kitchingroup.cheme.cmu.edu > > > > On Sat, Mar 3, 2018 at 12:26 PM, Thorsten Jolitz > > wrote: > > > > Thorsten Jolitz writes: > > > > PS > > One more to show that one can not only easily modify a certain > > org element, but that its just as easy to convert it to another type > > of > > org element. > > > > Use this (call M-x tj/obch) > > > > #+BEGIN_SRC emacs-lisp > > (defun tj/obch () > > "docstring" > > (interactive) > > (org-dp-rewire 'example-block t t ;cont ins > > '(:caption (("val2" "key2") ("val2" "key2")) > > :attr_xyz ("val1" "val2")) ;aff > > nil ;elem > > :language "common-lisp" > > :switches '(lambda (old elem) old ) > > :parameters 'tj/toggle-params > > :value '(lambda (old elem) > > (let ((old1 > > (string-remove-suffix "\n" old))) > > (concat "(+ 3 " old1 " 17)\n"))) > > :preserve-indent '(lambda (old elem) old ) ) ) > > #+END_SRC > > > > with point on this source block header > > > > ,---- > > | * test > > | > > | #+NAME: test1 > > | #+BEGIN_SRC emacs-lisp :tangle yes :results none > > | (+ 1 1) > > | #+END_SRC > > `---- > > > > to get this > > > > ,---- > > | #+NAME: test1 > > | #+CAPTION[key2]: val2 > > | #+CAPTION[key2]: val2 > > | #+ATTR_XYZ: val2 > > | #+ATTR_XYZ: val1 > > | #+BEGIN_EXAMPLE > > | (+ 3 (+ 1 1) 17) > > | #+END_EXAMPLE > > `---- > > > > > John Kitchin writes: > > > > > > Hallo, > > > > > >> This is a neat idea. > > > > > > This is quite a nice use/show case for org-dp too. > > > > > > I did not really try to solve the users feature request, just > > wanted to > > > demonstrate how different a possible solution looks using > > declarative > > > programming, leaving all the low-level parsing and interpreting > > work to > > > the org-element framework. > > > > > > 1. Example org-mode buffer > > > > > > ,---- > > > | * test > > > | > > > | #+NAME: test1 > > > | #+BEGIN_SRC emacs-lisp :tangle yes :results none > > > | (+ 1 1) > > > | #+END_SRC > > > | > > > | #+NAME: test2 > > > | #+BEGIN_SRC picolisp :tangle no :results raw > > > | (+ 2 2) > > > | #+END_SRC > > > `---- > > > > > > 2. Elisp to toggle the parameter values > > > > > > The org-dp part is this. > > > > > > Call the mapping cmd (M-x tj/obch-map) in the buffer (act on all > > > src-blocks), or put point on a src-block header and call M-x > > tj/obch to > > > just act on that scr-block. > > > > > > ,---- > > > | (defun tj/obch () > > > | "docstring" > > > | (interactive) > > > | (org-dp-rewire 'src-block t t ;cont ins > > > | t ;aff > > > | nil ;elem > > > | :language '(lambda (old elem) old ) > > > | :switches '(lambda (old elem) old ) > > > | :parameters 'tj/toggle-params > > > | :value '(lambda (old elem) old ) > > > | :preserve-indent '(lambda (old elem) old ) ) ) > > > | > > > | > > > | (defun tj/obch-map () > > > | "docstring" > > > | (interactive) > > > | (org-dp-map '(tj/obch) "#\\+BEGIN_SRC")) > > > `---- > > > > > > You can play around with the other args to org-dp-rewire (apart > > from > > > :parameters) to find out how easy you can change (or remove/add) > > other > > > parts of the src-block without any work on the textual > > representation. > > > > > > E.g. try this: > > > > > > #+BEGIN_SRC emacs-lisp > > > (defun tj/obch () > > > "docstring" > > > (interactive) > > > (org-dp-rewire 'src-block t t ;cont ins > > > nil ;aff > > > nil ;elem > > > :language "common-lisp" > > > :switches '(lambda (old elem) old ) > > > :parameters 'tj/toggle-params > > > :value '(lambda (old elem) > > > (let ((old1 > > > (string-remove-suffix "\n" old))) > > > (concat "(+ 3 " old1 " 17)\n"))) > > > :preserve-indent '(lambda (old elem) old ) ) ) > > > #+END_SRC > > > > > > > > > to see this result in the example buffer after calling M-x > > tj/obch-map: > > > > > > ,---- > > > | * test > > > | > > > | #+BEGIN_SRC common-lisp :tangle no :results raw > > > | (+ 3 (+ 1 1) 17) > > > | #+END_SRC > > > | > > > | #+BEGIN_SRC common-lisp :tangle yes :results none > > > | (+ 3 (+ 2 2) 17) > > > | #+END_SRC > > > `---- > > > > > > PS > > > Here is the whole code. > > > The logic in 'tj/toggle-params is not really of interest here. The > > > important thing is, that all of these options are possible: > > > > > > - simply assign a value > > > - implement a lambda function in place (with two args) > > > - implement a named function (with two args) and use its name > > > > > > ,---- > > > | :parameters ":tangle no" > > > | :parameters '(lambda (old elem) (concat old " :results none") ) > > > | :parameters 'tj/toggle-params > > > `---- > > > > > > #+BEGIN_SRC emacs-lisp > > > (defvar tj/change-p) > > > > > > ;; org-dp in action > > > ;; wrap org-dp-rewire in utility cmd for readability > > > (defun tj/obch () > > > "docstring" > > > (interactive) > > > (org-dp-rewire 'src-block t t ;cont ins > > > t ;aff > > > nil ;elem > > > :language '(lambda (old elem) old ) > > > :switches '(lambda (old elem) old ) > > > :parameters 'tj/toggle-params > > > :value '(lambda (old elem) old ) > > > :preserve-indent '(lambda (old elem) old ) ) ) > > > > > > > > > (defun tj/obch-map () > > > "docstring" > > > (interactive) > > > (org-dp-map '(tj/obch) "#\\+BEGIN_SRC")) > > > > > > ;; helper functions for this use case, not really of interest > > > ;; toggle src-block parameter values > > > (defun tj/toggle-params (old elem) > > > "docstring" > > > (let* ((params-lst (split-string old))) > > > (setq tj/change-p nil) > > > (mapconcat 'tj/replace-vals params-lst " ")) ) > > > > > > ;; helper functon to actually replace old with new values > > > (defun tj/replace-vals (strg) > > > "docstring" > > > (let (res) > > > (if tj/change-p > > > (progn > > > (cond > > > ((string-equal strg "yes") > > > (setq res "no")) > > > ((string-equal strg "no") > > > (setq res "yes")) > > > ((string-equal strg "none") > > > (setq res "raw")) > > > ((string-equal strg "raw") > > > (setq res "none")) ) > > > (setq tj/change-p nil) > > > res) > > > (cond > > > ((string-equal strg ":tangle") > > > (setq tj/change-p t)) > > > ((string-equal strg ":results") > > > (setq tj/change-p t))) > > > strg))) > > > #+END_SRC > > > > > > > > >> I sometimes want to switch to silent, or between > > >> value and results. I don't know if you would consider the code > > below an > > >> improvement, but it seems to do what you want, and is shorter. It > > has > > >> less checking of things, and is more of a replace the header kind > > of > > >> approach. > > >> > > >> Personally, I think strings are the way to go here. > > >> > > >> #+BEGIN_SRC emacs-lisp :tangle yes :results none > > >> (require 's) > > >> (require 'dash) > > >> > > >> (defvar header-sequences '((emacs-lisp . (":tangle no :results > > none" ;; > > >> type 2 above > > >> ":tangle yes :results none" ;; type 3 above > > >> ":results type verbatim" ;; type 1 above > > >> )))) > > >> > > >> (defun obch () > > >> (interactive) > > >> (let* ((lang (car (org-babel-get-src-block-info t))) > > >> (headers (cdr (assoc (intern-soft lang) header-sequences))) > > >> header index) > > >> (save-excursion > > >> (org-babel-goto-src-block-head) > > >> (re-search-forward lang) > > >> (setq header (buffer-substring-no-properties (point) > > >> (line-end-position)) > > >> index (-find-index (lambda (s) (string=3D (s-trim s) (s-trim > > header))) > > >> headers)) > > >> (delete-region (point) (line-end-position)) > > >> (insert " " (if index > > >> (nth (mod (+ 1 index) (length headers)) headers) > > >> (car headers)))))) > > >> #+END_SRC > > >> > > >> John > > >> > > >> ----------------------------------- > > >> Professor John Kitchin > > >> Doherty Hall A207F > > >> Department of Chemical Engineering > > >> Carnegie Mellon University > > >> Pittsburgh, PA 15213 > > >> 412-268-7803 > > >> @johnkitchin > > >> http://kitchingroup.cheme.cmu.edu > > >> > > >> On Wed, Feb 28, 2018 at 2:59 AM, Akater > > wrote: > > >> > > >> 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: > > >> - examples/test cases/desiderata, like > > >> `(my-implemented-or-desired-function x y)' (type 1) > > >> - drafts, failed attempts at implementations and other snippets > > >> better > > >> left as is, or as a warning (type 2) > > >> - 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=9Clit= erate > > >> 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' provides 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 code block. If current src-block header is not found in > > >> `header-strings', switch header to the car of `header-strings'. > > >> > > >> `header-strings' must be a non-empty list of strings. All > > whitespace > > >> in them 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 > > >> found intstead." header-strings)) > > >> ((not (every #'stringp header-strings)) > > >> (error "Malformed list of Org Babel header strings: not all > > elements > > >> are 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) > > >> (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") > > >> (:results . "none")) > > >> ((:tangle . "yes") > > >> (:results . "none")) > > >> ((:results . "type verbatim")))) > > >> (python > > >> (((:tangle . "no") > > >> (:results . "none")) > > >> ((:tangle . "yes") > > >> (:results . "none")) > > >> ((:results . "type output")))) > > >> ) > > >> (development-setup-2 > > >> (C > > >> (((:tangle . "no") > > >> (:results . "none")) > > >> ((:tangle . "yes") > > >> (:results . "raw")))) > > >> (julia > > >> (((:tangle . "no") > > >> (:results . "none")) > > >> ((:tangle . "yes") > > >> (:results . "none"))))))) > > >> #+end_src > > >> > > >> > > > > -- > > cheers, > > Thorsten > > > > > > -- > cheers, > Thorsten > > > --001a114989847de7f40566a28ada Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I guess this is a feature of deleting a region with the po= int in it. This code, for example, does not preserve point.

<= div>
#+BEGIN_SRC emacs-lisp
"<>"
(s= ave-excursion=C2=A0
=C2=A0 (let* ((p1 (point))
(p2 (re-search-backward (concat "<= " ">")))
(content (buffer-substring-no-properties p1 p2)))
=C2=A0 =C2= =A0 (delete-region p1 p2)
=C2=A0 =C2=A0 (insert content)))
<= div>#+END_SRC

John

------------= -----------------------
Professor John Kitchin=C2=A0
Doherty Hall A20= 7F
Department of Chemical Engineering
Carnegie Mellon University
P= ittsburgh, PA 15213
412-268-7803
@johnkitchin
<= a href=3D"http://kitchingroup.cheme.cmu.edu" target=3D"_blank">http://kitch= ingroup.cheme.cmu.edu


On Sun, Mar 4, 2018 at 4:21 PM, Thorsten Jol= itz <tjolitz@gmail.com> wrote:
John Kitchin <jkitchin@andrew.cmu.edu> writes:

> Thanks for the examples.
>
> There is an interesting issue, the following does not save-excursion!<= br> >
> (save-excursion
> (org-dp-rewire 'src-block t t ;cont ins
> t ;aff
> nil ;elem
> :parameters ":results output"))
>
> The point gets moved. Do you know why that happens?

Hmm ... org-dp-rewire is mostly fidling around with lists, but in th= e
end it acts conditionally on the 'replace' parameter:

,----
|=C2=A0 =C2=A0 =C2=A0(if (and (marker-position beg)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (marker-position end))
|=C2=A0 =C2=A0 =C2=A0 =C2=A0(cl-case replace
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(append (save-excursion (goto-char end) = (insert strg)))
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(prepend (goto-char beg) (insert strg))<= br> |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(t (if (not replace)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 strg
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (delete-region beg end) |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char end)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (set-marker beg nil)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (set-marker paff nil)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (set-marker end nil)
|=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (save-excursion (insert s= trg)))))
|=C2=A0 =C2=A0 =C2=A0 =C2=A0(if replace (insert strg) strg))))
`----

append or prepend result, return it as string, or replace the rewired
element.
I guess the is a save-excursion missing here ...

> John
>
> -----------------------------------
> Professor John Kitchin
> Doherty Hall A207F
> Department of Chemical Engineering
> Carnegie Mellon University
> Pittsburgh, PA 15213
> 412-268-7803 > @johnkitchin
> http://kitchingroup.cheme.cmu.edu
>
> On Sat, Mar 3, 2018 at 12:26 PM, Thorsten Jolitz <tjolitz@gmail.com>
> wrote:
>
>=C2=A0 Thorsten Jolitz <tjolitz= @gmail.com> writes:
>
>=C2=A0 PS
>=C2=A0 One more to show that one can not only easily modify a certain >=C2=A0 org element, but that its just as easy to convert it to another = type
>=C2=A0 of
>=C2=A0 org element.
>
>=C2=A0 Use this (call M-x tj/obch)
>
>=C2=A0 #+BEGIN_SRC emacs-lisp
>=C2=A0 (defun tj/obch ()
>=C2=A0 "docstring"
>=C2=A0 (interactive)
>=C2=A0 (org-dp-rewire 'example-block t t ;cont ins
>=C2=A0 '(:caption (("val2" "key2") ("val2&= quot; "key2"))
>=C2=A0 :attr_xyz ("val1" "val2")) ;aff
>=C2=A0 nil ;elem
>=C2=A0 :language "common-lisp"
>=C2=A0 :switches '(lambda (old elem) old )
>=C2=A0 :parameters 'tj/toggle-params
>=C2=A0 :value '(lambda (old elem)
>=C2=A0 (let ((old1
>=C2=A0 (string-remove-suffix "\n" old)))
>=C2=A0 (concat "(+ 3 " old1 " 17)\n")))
>=C2=A0 :preserve-indent '(lambda (old elem) old ) ) )
>=C2=A0 #+END_SRC
>
>=C2=A0 with point on this source block header
>
>=C2=A0 ,----
>=C2=A0 | * test
>=C2=A0 |
>=C2=A0 | #+NAME: test1
>=C2=A0 | #+BEGIN_SRC emacs-lisp :tangle yes :results none
>=C2=A0 | (+ 1 1)
>=C2=A0 | #+END_SRC
>=C2=A0 `----
>
>=C2=A0 to get this
>
>=C2=A0 ,----
>=C2=A0 | #+NAME: test1
>=C2=A0 | #+CAPTION[key2]: val2
>=C2=A0 | #+CAPTION[key2]: val2
>=C2=A0 | #+ATTR_XYZ: val2
>=C2=A0 | #+ATTR_XYZ: val1
>=C2=A0 | #+BEGIN_EXAMPLE
>=C2=A0 | (+ 3 (+ 1 1) 17)
>=C2=A0 | #+END_EXAMPLE
>=C2=A0 `----
>
>=C2=A0 > John Kitchin <jkitchin@andrew.cmu.edu> writes:
>=C2=A0 >
>=C2=A0 > Hallo,
>=C2=A0 >
>=C2=A0 >> This is a neat idea.
>=C2=A0 >
>=C2=A0 > This is quite a nice use/show case for org-dp too.
>=C2=A0 >
>=C2=A0 > I did not really try to solve the users feature request, ju= st
>=C2=A0 wanted to
>=C2=A0 > demonstrate how different a possible solution looks using >=C2=A0 declarative
>=C2=A0 > programming, leaving all the low-level parsing and interpre= ting
>=C2=A0 work to
>=C2=A0 > the org-element framework.
>=C2=A0 >
>=C2=A0 > 1. Example org-mode buffer
>=C2=A0 >
>=C2=A0 > ,----
>=C2=A0 > | * test
>=C2=A0 > |
>=C2=A0 > | #+NAME: test1
>=C2=A0 > | #+BEGIN_SRC emacs-lisp :tangle yes :results none
>=C2=A0 > | (+ 1 1)
>=C2=A0 > | #+END_SRC
>=C2=A0 > |
>=C2=A0 > | #+NAME: test2
>=C2=A0 > | #+BEGIN_SRC picolisp :tangle no :results raw
>=C2=A0 > | (+ 2 2)
>=C2=A0 > | #+END_SRC
>=C2=A0 > `----
>=C2=A0 >
>=C2=A0 > 2. Elisp to toggle the parameter values
>=C2=A0 >
>=C2=A0 > The org-dp part is this.
>=C2=A0 >
>=C2=A0 > Call the mapping cmd (M-x tj/obch-map) in the buffer (act o= n all
>=C2=A0 > src-blocks), or put point on a src-block header and call M-= x
>=C2=A0 tj/obch to
>=C2=A0 > just act on that scr-block.
>=C2=A0 >
>=C2=A0 > ,----
>=C2=A0 > | (defun tj/obch ()
>=C2=A0 > | "docstring"
>=C2=A0 > | (interactive)
>=C2=A0 > | (org-dp-rewire 'src-block t t ;cont ins
>=C2=A0 > | t ;aff
>=C2=A0 > | nil ;elem
>=C2=A0 > | :language '(lambda (old elem) old )
>=C2=A0 > | :switches '(lambda (old elem) old )
>=C2=A0 > | :parameters 'tj/toggle-params
>=C2=A0 > | :value '(lambda (old elem) old )
>=C2=A0 > | :preserve-indent '(lambda (old elem) old ) ) )
>=C2=A0 > |
>=C2=A0 > |
>=C2=A0 > | (defun tj/obch-map ()
>=C2=A0 > | "docstring"
>=C2=A0 > | (interactive)
>=C2=A0 > | (org-dp-map '(tj/obch) "#\\+BEGIN_SRC")) >=C2=A0 > `----
>=C2=A0 >
>=C2=A0 > You can play around with the other args to org-dp-rewire (a= part
>=C2=A0 from
>=C2=A0 > :parameters) to find out how easy you can change (or remove= /add)
>=C2=A0 other
>=C2=A0 > parts of the src-block without any work on the textual
>=C2=A0 representation.
>=C2=A0 >
>=C2=A0 > E.g. try this:
>=C2=A0 >
>=C2=A0 > #+BEGIN_SRC emacs-lisp
>=C2=A0 > (defun tj/obch ()
>=C2=A0 > "docstring"
>=C2=A0 > (interactive)
>=C2=A0 > (org-dp-rewire 'src-block t t ;cont ins
>=C2=A0 > nil ;aff
>=C2=A0 > nil ;elem
>=C2=A0 > :language "common-lisp"
>=C2=A0 > :switches '(lambda (old elem) old )
>=C2=A0 > :parameters 'tj/toggle-params
>=C2=A0 > :value '(lambda (old elem)
>=C2=A0 > (let ((old1
>=C2=A0 > (string-remove-suffix "\n" old)))
>=C2=A0 > (concat "(+ 3 " old1 " 17)\n")))
>=C2=A0 > :preserve-indent '(lambda (old elem) old ) ) )
>=C2=A0 > #+END_SRC
>=C2=A0 >
>=C2=A0 >
>=C2=A0 > to see this result in the example buffer after calling M-x<= br> >=C2=A0 tj/obch-map:
>=C2=A0 >
>=C2=A0 > ,----
>=C2=A0 > | * test
>=C2=A0 > |
>=C2=A0 > | #+BEGIN_SRC common-lisp :tangle no :results raw
>=C2=A0 > | (+ 3 (+ 1 1) 17)
>=C2=A0 > | #+END_SRC
>=C2=A0 > |
>=C2=A0 > | #+BEGIN_SRC common-lisp :tangle yes :results none
>=C2=A0 > | (+ 3 (+ 2 2) 17)
>=C2=A0 > | #+END_SRC
>=C2=A0 > `----
>=C2=A0 >
>=C2=A0 > PS
>=C2=A0 > Here is the whole code.
>=C2=A0 > The logic in 'tj/toggle-params is not really of interes= t here. The
>=C2=A0 > important thing is, that all of these options are possible:=
>=C2=A0 >
>=C2=A0 > - simply assign a value
>=C2=A0 > - implement a lambda function in place (with two args)
>=C2=A0 > - implement a named function (with two args) and use its na= me
>=C2=A0 >
>=C2=A0 > ,----
>=C2=A0 > | :parameters ":tangle no"
>=C2=A0 > | :parameters '(lambda (old elem) (concat old " :r= esults none") )
>=C2=A0 > | :parameters 'tj/toggle-params
>=C2=A0 > `----
>=C2=A0 >
>=C2=A0 > #+BEGIN_SRC emacs-lisp
>=C2=A0 > (defvar tj/change-p)
>=C2=A0 >
>=C2=A0 > ;; org-dp in action
>=C2=A0 > ;; wrap org-dp-rewire in utility cmd for readability
>=C2=A0 > (defun tj/obch ()
>=C2=A0 > "docstring"
>=C2=A0 > (interactive)
>=C2=A0 > (org-dp-rewire 'src-block t t ;cont ins
>=C2=A0 > t ;aff
>=C2=A0 > nil ;elem
>=C2=A0 > :language '(lambda (old elem) old )
>=C2=A0 > :switches '(lambda (old elem) old )
>=C2=A0 > :parameters 'tj/toggle-params
>=C2=A0 > :value '(lambda (old elem) old )
>=C2=A0 > :preserve-indent '(lambda (old elem) old ) ) )
>=C2=A0 >
>=C2=A0 >
>=C2=A0 > (defun tj/obch-map ()
>=C2=A0 > "docstring"
>=C2=A0 > (interactive)
>=C2=A0 > (org-dp-map '(tj/obch) "#\\+BEGIN_SRC"))
>=C2=A0 >
>=C2=A0 > ;; helper functions for this use case, not really of intere= st
>=C2=A0 > ;; toggle src-block parameter values
>=C2=A0 > (defun tj/toggle-params (old elem)
>=C2=A0 > "docstring"
>=C2=A0 > (let* ((params-lst (split-string old)))
>=C2=A0 > (setq tj/change-p nil)
>=C2=A0 > (mapconcat 'tj/replace-vals params-lst " ")) = )
>=C2=A0 >
>=C2=A0 > ;; helper functon to actually replace old with new values >=C2=A0 > (defun tj/replace-vals (strg)
>=C2=A0 > "docstring"
>=C2=A0 > (let (res)
>=C2=A0 > (if tj/change-p
>=C2=A0 > (progn
>=C2=A0 > (cond
>=C2=A0 > ((string-equal strg "yes")
>=C2=A0 > (setq res "no"))
>=C2=A0 > ((string-equal strg "no")
>=C2=A0 > (setq res "yes"))
>=C2=A0 > ((string-equal strg "none")
>=C2=A0 > (setq res "raw"))
>=C2=A0 > ((string-equal strg "raw")
>=C2=A0 > (setq res "none")) )
>=C2=A0 > (setq tj/change-p nil)
>=C2=A0 > res)
>=C2=A0 > (cond
>=C2=A0 > ((string-equal strg ":tangle")
>=C2=A0 > (setq tj/change-p t))
>=C2=A0 > ((string-equal strg ":results")
>=C2=A0 > (setq tj/change-p t)))
>=C2=A0 > strg)))
>=C2=A0 > #+END_SRC
>=C2=A0 >
>=C2=A0 >
>=C2=A0 >> I sometimes want to switch to silent, or between
>=C2=A0 >> value and results. I don't know if you would consid= er the code
>=C2=A0 below an
>=C2=A0 >> improvement, but it seems to do what you want, and is s= horter. It
>=C2=A0 has
>=C2=A0 >> less checking of things, and is more of a replace the h= eader kind
>=C2=A0 of
>=C2=A0 >> approach.
>=C2=A0 >>
>=C2=A0 >> Personally, I think strings are the way to go here.
>=C2=A0 >>
>=C2=A0 >> #+BEGIN_SRC emacs-lisp :tangle yes :results none
>=C2=A0 >> (require 's)
>=C2=A0 >> (require 'dash)
>=C2=A0 >>
>=C2=A0 >> (defvar header-sequences '((emacs-lisp . (":ta= ngle no :results
>=C2=A0 none" ;;
>=C2=A0 >> type 2 above
>=C2=A0 >> ":tangle yes :results none" ;; type 3 above >=C2=A0 >> ":results type verbatim" ;; type 1 above
>=C2=A0 >> ))))
>=C2=A0 >>
>=C2=A0 >> (defun obch ()
>=C2=A0 >> (interactive)
>=C2=A0 >> (let* ((lang (car (org-babel-get-src-block-info t))) >=C2=A0 >> (headers (cdr (assoc (intern-soft lang) header-sequence= s)))
>=C2=A0 >> header index)
>=C2=A0 >> (save-excursion
>=C2=A0 >> (org-babel-goto-src-block-head)
>=C2=A0 >> (re-search-forward lang)
>=C2=A0 >> (setq header (buffer-substring-no-properties (poin= t)
>=C2=A0 >> (line-end-position))
>=C2=A0 >> index (-find-index (lambda (s) (string=3D (s-trim s) (s= -trim
>=C2=A0 header)))
>=C2=A0 >> headers))
>=C2=A0 >> (delete-region (point) (line-end-position))
>=C2=A0 >> (insert " " (if index
>=C2=A0 >> (nth (mod (+ 1 index) (length headers)) headers)
>=C2=A0 >> (car headers))))))
>=C2=A0 >> #+END_SRC
>=C2=A0 >>
>=C2=A0 >> John
>=C2=A0 >>
>=C2=A0 >> -----------------------------------
>=C2=A0 >> Professor John Kitchin
>=C2=A0 >> Doherty Hall A207F
>=C2=A0 >> Department of Chemical Engineering
>=C2=A0 >> Carnegie Mellon University
>=C2=A0 >> Pittsburgh, PA 15213
>=C2=A0 >> 412-268-7803
>=C2=A0 >> @johnkitchin
>=C2=A0 >> http://kitchingroup.cheme.cmu.edu
>=C2=A0 >>
>=C2=A0 >> On Wed, Feb 28, 2018 at 2:59 AM, Akater <nuclearspace@gmail.com>
>=C2=A0 wrote:
>=C2=A0 >>
>=C2=A0 >> When I have a chance, I enjoy the following development= workflow:
>=C2=A0 >> the
>=C2=A0 >> code is written in org files and is tangled into conven= tional
>=C2=A0 source
>=C2=A0 >> code files more or less regularly.
>=C2=A0 >>
>=C2=A0 >> I find that source blocks mostly fall into three catego= ries,
>=C2=A0 >> numbered
>=C2=A0 >> here for further reference:
>=C2=A0 >> - examples/test cases/desiderata, like
>=C2=A0 >> `(my-implemented-or-desired-function x y)' (ty= pe 1)
>=C2=A0 >> - drafts, failed attempts at implementations and other = snippets
>=C2=A0 >> better
>=C2=A0 >> left as is, or as a warning (type 2)
>=C2=A0 >> - working implementations, to be tangled (type 3)
>=C2=A0 >>
>=C2=A0 >> Hence I end up using only a handful of header argument = strings.
>=C2=A0 An
>=C2=A0 >> example corresponding to this 3-cases setup is found be= low. So it
>=C2=A0 >> would
>=C2=A0 >> be nice to have a function that cycles between those, m= uch like
>=C2=A0 we
>=C2=A0 >> can
>=C2=A0 >> cycle through org TODO sequence now using a standard fu= nction,
>=C2=A0 and
>=C2=A0 >> set
>=C2=A0 >> up this sequence per Org file.
>=C2=A0 >>
>=C2=A0 >> I'm fairly bad at Emacs Lisp, so I'm interested= in feedback about
>=C2=A0 my
>=C2=A0 >> implementation of cycling function. It operates with st= rings,
>=C2=A0 mostly
>=C2=A0 >> because I failed to make it work with lists of alists o= f header
>=C2=A0 >> arguments as ob-core.el suggests. On the other hand, gi= ven that
>=C2=A0 >> Emacs
>=C2=A0 >> Lisp is more string-oriented than it is object-oriented= , it might
>=C2=A0 >> not be
>=C2=A0 >> a really bad idea.
>=C2=A0 >>
>=C2=A0 >> So what do you think? How can this implementation be im= proved?
>=C2=A0 (Sans
>=C2=A0 >> using rotate and tracking position in a smarter way.) D= oes it
>=C2=A0 make
>=C2=A0 >> sense
>=C2=A0 >> to include this feature in Org mode? Maybe I missed som= e existing
>=C2=A0 >> well-estabilished solutions? This is something akin to = =E2=80=9Cliterate
>=C2=A0 >> programming=E2=80=9D; I'm not a fan of this idea---= at least the way it is
>=C2=A0 >> usually presented---but it is somewhat popular a topic.= I have
>=C2=A0 some
>=C2=A0 >> other feature in mind I'd love to see implemented i= n Org-Babel:
>=C2=A0 >> convenient export of src blocks of type 1 (see above) i= nto unit
>=C2=A0 >> tests
>=C2=A0 >> (as test cases) and into documentation sources (as exam= ples) but
>=C2=A0 >> this
>=C2=A0 >> one is heavily target-language dependent and probably d= eserves
>=C2=A0 its
>=C2=A0 >> own
>=C2=A0 >> thread.
>=C2=A0 >>
>=C2=A0 >> #+begin_src emacs-lisp
>=C2=A0 >> (cl-defun next-maybe-cycled (elem list &key (test #= 'equal))
>=C2=A0 >> "Returns the element in `list' next to the fir= st `elem' found. If
>=C2=A0 >> `elem' is found at `list''s very tail, retu= rns `list''s car.
>=C2=A0 >> `next-maybe-cycled' provides no way to distinguish = between
>=C2=A0 \"found
>=C2=A0 >> nil\" and \"found nothing\"."
>=C2=A0 >> (let ((sublist (cl-member elem list :test test)))
>=C2=A0 >> (and sublist
>=C2=A0 >> (if (cdr sublist)
>=C2=A0 >> (cadr sublist)
>=C2=A0 >> (car list)))))
>=C2=A0 >>
>=C2=A0 >> (defun shrink-whitespace (string)
>=C2=A0 >> "Transforms all whitespace instances into single s= paces. Trims
>=C2=A0 >> whitespace at beginning and end. No argument type check= ing."
>=C2=A0 >> (cl-reduce (lambda (string rule)
>=C2=A0 >> (replace-regexp-in-string (car rule) (cdr rule) string)= )
>=C2=A0 >> '(("[[:blank:]]+" . " ") ("= ;^[[:blank:]]*" . "") ("[[:blank:]]*$"
>=C2=A0 .
>=C2=A0 >> ""))
>=C2=A0 >> :initial-value string))
>=C2=A0 >>
>=C2=A0 >> (defun string-equal-modulo-whitespace (x y)
>=C2=A0 >> (string-equal (shrink-whitespace x) (shrink-whitespace = y)))
>=C2=A0 >>
>=C2=A0 >> (defun org-babel-cycle-src-block-header-string (he= ader-strings)
>=C2=A0 >> "Cycle through given `header-strings' if curre= ntly in Org Babel
>=C2=A0 >> source code block. If current src-block header is not f= ound in
>=C2=A0 >> `header-strings', switch header to the car of `head= er-strings'.
>=C2=A0 >>
>=C2=A0 >> `header-strings' must be a non-empty list of string= s. All
>=C2=A0 whitespace
>=C2=A0 >> in them is shrinked.
>=C2=A0 >>
>=C2=A0 >> If UNDO-ed, cursor position is not guaranteed to be pre= served."
>=C2=A0 >> (interactive)
>=C2=A0 >> (cond
>=C2=A0 >> ((not (and header-strings (listp header-strings)))
>=C2=A0 >> (error "No Org Babel header strings list found to = cycle through.
>=C2=A0 %S
>=C2=A0 >> found intstead." header-strings))
>=C2=A0 >> ((not (every #'stringp header-strings))
>=C2=A0 >> (error "Malformed list of Org Babel header strings= : not all
>=C2=A0 elements
>=C2=A0 >> are strings in %S." header-strings))
>=C2=A0 >> (t
>=C2=A0 >> (let ((initial-position (point)))
>=C2=A0 >> (org-babel-goto-src-block-head)
>=C2=A0 >> ;; here we rely on `org-babel-goto-src-block-head&= #39;
>=C2=A0 >> ;; signalling an error if not in source code block
>=C2=A0 >> (forward-char (length "#+BEGIN_SRC"))
>=C2=A0 >> (let* ((fallback-position (point))
>=C2=A0 >> (we-were-before-replacement-zone (<=3D initial-= position
>=C2=A0 >> fallback-position)))
>=C2=A0 >> (let ((default-position-to-return-to initial-posit= ion)
>=C2=A0 >> (old-header-string (delete-and-extract-region (point) >=C2=A0 >> (line-end-position))))
>=C2=A0 >> (unless we-were-before-replacement-zone
>=C2=A0 >> (incf default-position-to-return-to (- (length
>=C2=A0 old-header-string))))
>=C2=A0 >> (let ((new-header-string
>=C2=A0 >> (concatenate 'string
>=C2=A0 >> " "
>=C2=A0 >> (shrink-whitespace
>=C2=A0 >> (or (next-maybe-cycled old-header-string
>=C2=A0 >> header-strings
>=C2=A0 >> :test #'string-equal-modulo-whitespace)
>=C2=A0 >> (car header-strings))))))
>=C2=A0 >> (insert new-header-string)
>=C2=A0 >> (unless we-were-before-replacement-zone
>=C2=A0 >> (incf default-position-to-return-to (length new-header-= string)))
>=C2=A0 >> (goto-char (if (<=3D fallback-position
>=C2=A0 >> default-position-to-return-to
>=C2=A0 >> (+ fallback-position (length new-header-string)))
>=C2=A0 >> fallback-position
>=C2=A0 >> default-position-to-return-to)))))))))
>=C2=A0 >>
>=C2=A0 >> ;; example for mailing list
>=C2=A0 >> ;; Common Lisp assumed!
>=C2=A0 >> (defun akater/org-babel-cycle-header nil
>=C2=A0 >> (interactive)
>=C2=A0 >> (org-babel-cycle-src-block-header-string
>=C2=A0 >> '("lisp :tangle no :results none" ;; type= 2 above
>=C2=A0 >> "lisp :tangle yes :results none" ;; type 3 ab= ove
>=C2=A0 >> "lisp :results type verbatim" ;; type 1 above=
>=C2=A0 >> )))
>=C2=A0 >> #+end_src
>=C2=A0 >>
>=C2=A0 >> Ideally, I envision something along these lines (some s= pecific
>=C2=A0 >> choices
>=C2=A0 >> below don't really make sense):
>=C2=A0 >> #+begin_src emacs-lisp
>=C2=A0 >> (defcustom org-babel-standard-header-sequences-ali= st
>=C2=A0 >> '((development-setup-1
>=C2=A0 >> (lisp
>=C2=A0 >> (((:tangle . "no")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:tangle . "yes")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:results . "type verbatim"))))
>=C2=A0 >> (python
>=C2=A0 >> (((:tangle . "no")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:tangle . "yes")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:results . "type output"))))
>=C2=A0 >> )
>=C2=A0 >> (development-setup-2
>=C2=A0 >> (C
>=C2=A0 >> (((:tangle . "no")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:tangle . "yes")
>=C2=A0 >> (:results . "raw"))))
>=C2=A0 >> (julia
>=C2=A0 >> (((:tangle . "no")
>=C2=A0 >> (:results . "none"))
>=C2=A0 >> ((:tangle . "yes")
>=C2=A0 >> (:results . "none")))))))
>=C2=A0 >> #+end_src
>=C2=A0 >>
>=C2=A0 >>
>
>=C2=A0 --
>=C2=A0 cheers,
>=C2=A0 Thorsten
>
>

--
cheers,
Thorsten



--001a114989847de7f40566a28ada--