7

How one can programmatically read and set a buffer-wide org-mode property?

I am aware of this, which points to

(org-entry-put POM "PWE-1" "47")

but it does not seem useful in case of buffer global properties.

Details:

If a file has buffer global properties, like,

#+PROPERTY: PWE-1 42
#+PROPERTY: Cite "Silmarillon 1984"
#+PROPERTY: my/common-list (1 "two" (1 2 3 5 7))

How could one fetch, for example, the buffer-wide property PWE-1, or any other?

How would one set it, ie, write a different value, say

#+PROPERTY: PWE-1 2197

so that the buffer will keep the updated value as stored in the file, available at next load?

Where could I look up similar information in detail? Is there a detailed list of org-mode's API?

9
  • There is org-element-parse-buffer. Maybe, that helps. As far as I remember, you get also the buffer positions of the parsed stuff. So you can replace the text for PWE-1. (Don't have time to check right now.) – Tobias Apr 6 '16 at 11:34
  • Thank you for the pointer. That page pointed to orgmode.org/worg/exporters/org-element-docstrings.html, but it does not seem to list a global #+PROPERTY as a parseable element? – gsl Apr 6 '16 at 11:47
  • 1
    Just do (setq p (org-element-parse-buffer)) and you will get (org-data nil (section (:begin 1 :end 108 :contents-begin 1 :contents-end 107 :post-blank 1 :parent #0) (keyword (:key "PROPERTY" :value "PWE-1 42" :begin 1 :end 22 :post-blank 0 :post-affiliated 1 :parent #1)) at the beginning of p if your example text is at the beginning of the current org-buffer. – Tobias Apr 6 '16 at 12:52
  • Wouldn't it be :PROPERTIES:, then :PWE-1: 42, then :END:? – mankoff Apr 6 '16 at 13:03
  • @mankoff That would be a drawer for the current tree. #+PROPERTY: PWE-1 42 is also fine. That's global. (See info files.) – Tobias Apr 6 '16 at 13:44
9

You could just do a text search for #+PROPERTY: ....

Alternatively, you can use the following functions. These functions take into account that values can be accumulated via + and it is an error if the first PROPERTY key has a +. (Is that true?)

(require 'cl-lib)

(defun org-global-props-key-re (key)
  "Construct a regular expression matching key and an optional plus and eating the spaces behind.
Test for existence of the plus: (match-beginning 1)"
  (concat "^" (regexp-quote key) "\\(\\+\\)?[[:space:]]+"))

(defun org-global-props (&optional buffer)
  "Get the plists of global org properties of current buffer."
  (with-current-buffer (or buffer (current-buffer))
    (org-element-map (org-element-parse-buffer) 'keyword (lambda (el) (when (string-equal (org-element-property :key el) "PROPERTY") (nth 1 el))))))

(defun org-global-prop-value (key)
  "Get global org property KEY of current buffer.
Adding up values for one key is supported."
  (let ((key-re (org-global-props-key-re key))
    (props (org-global-props))
    ret)
    (cl-loop with val for prop in props
         when (string-match key-re (setq val (plist-get prop :value))) do
         (setq
          val (substring val (match-end 0))
          ret (if (match-beginning 1)
              (concat ret " " val)
            val)))
    ret))

(defun org-global-prop-set (key value)
  "Set the value of the first occurence of
#+PROPERTY: KEY
add it at the beginning of file if there is none."
  (save-excursion
    (let* ((key-re (org-global-props-key-re key))
       (prop (cl-find-if (lambda (prop)
                   (string-match key-re (plist-get prop :value)))
                 (org-global-props))))
      (if prop
      (progn
        (assert (null (match-beginning 1)) "First occurence of key %s is followed by +." key)
        (goto-char (plist-get prop :begin))
        (kill-region (point) (plist-get prop :end)))
    (goto-char 1))
      (insert "#+PROPERTY: " key " " value "\n"))))
3
  • 1
    This is exactly what I was looking for. I am able to fetch and set a buffer-wide property just by specifying its name, ie (org-global-prop-value "pwe-1"), (org-global-prop-set "pwe-1" "77"). Your code is quite instructive, I am learning a lot. It should be included in org-mode API, I am sure I am not the only one to find it very useful! Thank you. – gsl Apr 7 '16 at 7:07
  • org-global-prop-value is quite slow – Toothrot Jan 28 '20 at 22:20
  • This is insanely difficult for such a common need. – RichieHH Dec 21 '20 at 12:40
2

(org-collect-keywords '("PROPERTY")) could also be helpful, as mentioned in https://emacs.stackexchange.com/a/63102/31740.

The org-collect-keywords doc string states:

Return values for KEYWORDS in current buffer, as an alist.

KEYWORDS is a list of strings. Return value is a list of elements with the pattern:

(NAME . LIST-OF-VALUES)

where NAME is the upcase name of the keyword, and LIST-OF-VALUES is a list of non-empty values, as strings, in order of appearance in the buffer.

This allows one to get the property values, but it does not allow setting them. In fact, Org mode does not provide any way of setting them, so @Tobias's answer still stands for the setting part: you basically have to edit the file, either by hand or programatically.

2
  • Please summarize the part of the content at that URL that you think answers the question. Otherwise, this risks being deleted as a link-only answer. Thx. – Drew Apr 28 at 23:18
  • done as requested – Venky Iyer Apr 30 at 18:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.