From mboxrd@z Thu Jan 1 00:00:00 1970 From: Thorsten Jolitz Subject: [FYI] Programming with org-dp (by example) Date: Mon, 05 Mar 2018 00:44:10 +0100 Message-ID: <877eqrs8xh.fsf@gmail.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:47778) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1esdIx-0000vy-6R for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 18:44:36 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1esdIu-0000jt-2L for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 18:44:35 -0500 Received: from [195.159.176.226] (port=57047 helo=blaine.gmane.org) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1esdIt-0000jW-RF for emacs-orgmode@gnu.org; Sun, 04 Mar 2018 18:44:31 -0500 Received: from list by blaine.gmane.org with local (Exim 4.84_2) (envelope-from ) id 1esdGk-000397-TH for emacs-orgmode@gnu.org; Mon, 05 Mar 2018 00:42:18 +0100 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 Hello List, due to some interest in org-dp recently on this list, I actually took the challenge of implementing a feature request by John Kitchin (without actually sticking close to the specification, this is just a showcase for org-dp). Task: implement a radio-list with org checkboxes Extra feature: a read only radio-list that changes its values conditional on the values of read/write radio-lists (when mapping the buffer) Example org buffer ,---- | * org-mode radio-list test | | #+NAME: temp | #+ATTR_ORG: :radio | - [ ] freezing | - [ ] cold | - [ ] normal | - [X] warm | - [ ] hot | - [ ] tropical | | #+NAME: wind | #+ATTR_ORG: :radio | - [X] breeze | - [ ] storm | - [ ] hurricane | | #+LABEL: read-only | #+NAME: take-a-walk | #+ATTR_ORG: :radio | - [ ] red | - [ ] yellow | - [X] green `---- This is the main program. The nice thing about this kind of declarative programming: - parsing and interpretation is left to the org-element framework - its very clear what we want/need to do: implement tj/radio-cont ,---- | ;; rewire function | (defun tj/radio-switch () | "docstring" | (interactive) | (let ((stmp (time-stamp-string))) | (forward-line) | (org-dp-rewire 'plain-list | 'tj/radio-cont ;cont | t ;ins | `(:attr_last_changed (,stmp)) ;aff | nil ;elem | ))) | | ;; mapping function | (defun tj/radio-map () | "docstring" | (interactive) | (let (temp wind) | (org-dp-map '(tj/radio-switch) tj/radio-rgxp))) `---- tj/radio-switch () is the 'rewire' function to modify the plain (checkbox) list at point. It does two things: 1. call function 'tj/radio-cont to modifiy the content (= items) of the plain list 2. add a new affiliated keyword with a current timestamp `(:attr_last_changed (,stmp)) tj/radio-map () is the mapping function Here is the result of switching to more extreme weather conditions by calling tj/radio-map and answering the user prompts two times: ,---- | #+NAME: temp | #+ATTR_ORG: :radio | #+ATTR_LAST_CHANGED: 2018-03-05 00:12:02 tj | - [ ] freezing | - [ ] cold | - [ ] normal | - [ ] warm | - [ ] hot | - [X] tropical | | #+NAME: wind | #+ATTR_ORG: :radio | #+ATTR_LAST_CHANGED: 2018-03-05 00:12:05 tj | - [ ] breeze | - [ ] storm | - [X] hurricane | | #+NAME: take-a-walk | #+ATTR_ORG: :radio | #+ATTR_LAST_CHANGED: 2018-03-05 00:12:10 tj | - [X] red | - [ ] yellow | - [ ] green `---- Emacs Lisp is very good for working with text, but with regards to org elements, whats needed for parsing and interpreting has already be written by Nicolas, why not reuse it and work on a higher level. So org-dp might be a light-weight alternative when acting locally on org elements (no need for a full parse tree). It produces pretty clean code, since working on text is replaced by working with lists. PS 1 There was a bug in org-dp.el, I fixed it locally but cannot push to origin due to github authentication problems (sight..), so if you want to try the code here, you need to change this too: ,---- | (cont (let ((orig-elem-cont (org-dp-contents elem))) | (cond | ;; ((and (consp contents) (functionp contents)) | ((and contents (functionp contents)) ... `---- magit diff of same change: ,---- | 1 file changed, 2 insertions(+), 1 deletion(-) | org-dp.el | 3 ++- | | modified org-dp.el | @@ -709,7 +709,8 @@ (cl-defun org-dp-rewire (elem-type &optional contents replace affiliated element | (make-marker) (org-element-property :end elem))) | (cont (let ((orig-elem-cont (org-dp-contents elem))) | (cond | - ((and (consp contents) (functionp contents)) | + ;; ((and (consp contents) (functionp contents)) | + ((and contents (functionp contents)) | (apply contents (list orig-elem-cont elem))) | ((and contents (booleanp contents)) | orig-elem-cont) `---- PS 2 here is the complete code: #+BEGIN_SRC emacs-lisp (defconst tj/radio-rgxp "^#\\+attr_org:[[:space:]]+:radio") (defconst tj/radio-temp "temp") (defconst tj/radio-wind "wind") (defvar tj/radio-rw '("temp" "wind")) ;read/write (defvar tj/radio-r '("take-a-walk")) ;read only (defvar tj/radio-temp-red '("freezing" "tropical")) (defvar tj/radio-temp-yellow '("cold" "hot")) (defvar tj/radio-temp-green '("normal" "warm")) (defvar tj/radio-wind-red '("breeze")) (defvar tj/radio-wind-yellow '("storm")) (defvar tj/radio-wind-green '("hurricane")) ;; rewire function (defun tj/radio-switch () "docstring" (interactive) (let ((stmp (time-stamp-string))) (forward-line) (org-dp-rewire 'plain-list 'tj/radio-cont ;cont t ;ins `(:attr_last_changed (,stmp)) ;aff nil ;elem ))) ;; mapping function (defun tj/radio-map () "docstring" (interactive) (let (temp wind) (org-dp-map '(tj/radio-switch) tj/radio-rgxp))) ;; HELPER FUNCTIONS ;; helper function to actually modify the content (defun tj/radio-cont (cont elem) "docstring" (let ((name (org-element-property :name elem)) (prompt-options (tj/radio-get-itm-labels cont)) (new-cont) (users-choice)) (cond ((member name tj/radio-rw) ;prompt user for value (progn (setq users-choice (ido-completing-read name prompt-options)) (set (intern name) users-choice) (setq new-cont (mapcar 'tj/radio-itm-rw cont)))) ((member name tj/radio-r) ;set value (setq new-cont (mapcar 'tj/radio-itm-r cont))) (t)) ;do nothing (or new-cont cont))) (defun tj/radio-get-itm-labels (cont) "docstring" (mapcar #'(lambda (itm) (string-remove-suffix "\n" (org-dp-contents itm t t))) cont)) (defun tj/radio-itm-rw (itm) "docstring" (let ((label (string-remove-suffix "\n" (org-dp-contents itm t t)))) (if (string-equal label users-choice) (org-element-put-property itm :checkbox 'on) (org-element-put-property itm :checkbox 'off)) itm)) (defun tj/radio-itm-r (itm) "docstring" (let ((label (string-remove-suffix "\n" (org-dp-contents itm t t)))) (org-element-put-property itm :checkbox 'off) (cond ((and (string-equal label "red") (or (member temp tj/radio-temp-red) (member temp tj/radio-wind-red))) (org-element-put-property itm :checkbox 'on)) ((and (string-equal label "yellow") (or (member temp tj/radio-temp-yellow) (member temp tj/radio-wind-yellow))) (org-element-put-property itm :checkbox 'on)) ((and (string-equal label "green") (or (member temp tj/radio-temp-green) (member temp tj/radio-wind-green))) (org-element-put-property itm :checkbox 'on))) itm)) #+END_SRC -- cheers, Thorsten