I'm not sure I'm more daring than @lawlist, but I'm almost certainly foolish for even encouraging you in this: after all, the X's are exactly there to mark those list items as done; so why delete them?
That said, here's some code that seems to work in the above situation (the only situation tested). The code does use kill-region
, so you should be able to undo your way back from disaster. Event though org-at-item-checkbox-p
still exists in the code, I did not use it, although a different solution could almost certainly be built around it, but I'm not sure it would be any simpler.
#+begin_src emacs-lisp :results drawer
(defun delx (items)
(while (not (null items))
(let ((item (car items)))
(if (equal (nth 4 item) "[X]")
(kill-region (nth 0 item) (car (last item)))))
(setq items (cdr items))))
(defun ndk/clean-up-shopping-list ()
(interactive)
(save-excursion
(let* ((elt (org-element-at-point))
(type (org-element-type elt)))
(when (eq type 'plain-list)
(let ((items (plist-get (cadr elt) :structure)))
(delx (reverse items)))))))
#+end_src
To invoke it, you put your cursor at the beginning of the list and say M-x ndk/clean-up-shopping-list
.
Some explanations:
the ndk/clean-up-shopping-list
function uses the org-element
parser to figure out whether it's at the beginning of a plain list. If so, it retrieves the list of items in the list, and calls the auxiliary function delx
with the reverse of the item list (see below for the reason).
delx
loops over the list of items and for each item, if the item is checked, it kills the region from the beginning to the end of the item.
the reason that we reverse the list before passing to delx
is that the list of items looks like this: ((126 0 "- " nil "[X]" nil 144) (144 0 "- " nil "[ ]" nil 158) (158 0 "- " nil "[X]" nil 169) (169 0 "- " nil "[ ]" nil 186) (186 0 "- " nil "[X]" nil 198))
, a list of items in increasing order of position; each item is a list of the form (beg _ _ _ "[x]" _ end)
where beg
is the character position of the beginning of the item, end
is the character position at the end of the item, _
indicates "don't care" stuff and "[x]"
can be either a checked box or an empty box. delx
figures out whether each item is checked or unchecked and if it is checked, it calls kill-region
with beg
and end
as arguments. But it has to process the list of items in reverse order, because if it starts deleting things from the top, the character positions of the later items will change, so we would not be able to use the beg/end
numbers. Doing the deletions from the end does not have that problem: the character positions of the earlier items do not change, so the beg/end
info from the item list remains valid.
BTW, note that the end position of each item is the same as the beginning position of the next item. So another way to do it is to delete from the beginning but then recalculate the beg/end
positions of subsequent items. That's not too difficult but I think it's messier, so I opted for deleting from the bottom.
org-at-item-checkbox-p
in an older version oforg-mode
, which may still exist in current releases.delete-region
does not save to thekill-ring
, whereaskill-region
does. ... I'll leave it up to another forum participant more daring than myself to write something up, if there is no built-in solution.