emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Thorsten Jolitz <tjolitz@gmail.com>
To: emacs-orgmode@gnu.org
Subject: [FYI] Programming with org-dp (by example)
Date: Mon, 05 Mar 2018 00:44:10 +0100	[thread overview]
Message-ID: <877eqrs8xh.fsf@gmail.com> (raw)


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

             reply	other threads:[~2018-03-04 23:44 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-04 23:44 Thorsten Jolitz [this message]
2018-03-05  0:12 ` [FYI] Programming with org-dp (by example) Thorsten Jolitz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877eqrs8xh.fsf@gmail.com \
    --to=tjolitz@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).