emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: org table integrity


From: John Kitchin
Subject: Re: org table integrity
Date: Sun, 2 Feb 2020 20:50:48 -0500

This is a little tricky to guarantee; there are just so many ways to edit a table.

One way to do this is like the following. The idea is to use the org-cycle-hook to check the last cell you were in when you pressed TAB in a table. This of course does not work if you don't use tab, and in its current form does not work on shift-tab.  You set an #+attr_org line to have list of predicate functions for each column. stringp is tricky, all the cells are strings, so I tried a regexp for numbers. numberp is also not quite right, for me "1a" converts to 1. This code is pretty lightly tested, so tread warily!

I had to add the advice for some reason. I don't know why, but it appears to me the org-cycle-hook does not get run inside org-cycle. I don't think this should be necessary, but it was to test this out.

Here is the code. After you run it, each time you tab through the table, you should see some messages telling you what was tested, and some of them should cause an error with a message about it.

#+BEGIN_SRC emacs-lisp
(defun org-timestamp-p (contents)
  "Return non-nil if CONTENTS is a legal org timestamp"
  (with-temp-buffer
    (insert contents)
    (goto-char (point-min))
    (re-search-forward org-element--timestamp-regexp nil t)))

(defun check-table-integrity (_state)
  (interactive)
  ;; get previous contents
  (let* ((column)
(element (org-element-context))
(attr)
(funcs)
(field)
(valid))

    (while (and (org-at-table-p) (not (eq 'table (car element)))
(setq element (org-element-property :parent element))))

    (when (eq 'table (car element))

      (setq attr (car (org-element-property :attr_org element))
   funcs (plist-get (read (format "(%s)" attr)) :types))

      (save-excursion
(org-table-previous-field)
(setq column (org-table-current-column)
     func (nth (- column 1) funcs)
     field (org-table-get-field)
     valid (funcall (eval func) (s-trim field)))

(message "checking %s field with %s" field func)

;; this is a little tricky. valid is non-nil, and 0 is considered non-nil
(unless valid
 (error "%s did not pass %s" field func))))))


(add-hook 'org-cycle-hook 'check-table-integrity)

(advice-add 'org-cycle :after (lambda (x) (cl-loop for func in org-cycle-hook
  do (funcall func ""))))
#+END_SRC

#+RESULTS:

#+attr_org: :types ('org-timestamp-p (lambda (x) (string-match-p "[0-9]+" x)) 'stringp)
| [2020-02-02 Sun] |  1 | no  |
| [2020-02-09 Sun] | no | yes |
| not a ts         |  3 | 3   |

An alternative way to do this might be to have a function that runs when you leave a table, then it could validate the cells in a similar way. The easy way to do this is with a post-command hook, but this is hard to do without performance hits. The harder way to do it is with cursor sensor functions, but this requires a hack on font-lock for tables. A final way is to make a save/kill-buffer hook function that would map over each table and validate them before allowing the save/kill to complete.



John

-----------------------------------
Professor John Kitchin 
Doherty Hall A207F
Department of Chemical Engineering
Carnegie Mellon University
Pittsburgh, PA 15213
412-268-7803


On Sat, Feb 1, 2020 at 9:09 PM Jude DaShiell <address@hidden> wrote:
Does a way exist in orgmode to fix a column so that it only stores time
stamps?  A table I'm using has two columns that could this kind of error
protection and two that should contain text.



--



reply via email to

[Prev in Thread] Current Thread [Next in Thread]