1

I'm working with an org-mode file that might contain headlines and text, like

* Headline 1
  :PROPERTIES:
  :ID: h1
  :END:
  This is some text.
** Subheading A
   :PROPERTIES:
   :ID: s1
   :END:
   The subheading can also have text.

Suppose I have a string bound to the symbol mytext that looks like

:PROPERTIES:
:ID: h1
:END:
This is some UPDATED text.

As you can see, it can
be multlined.

I want to replace h1's "body" text with the stuff in mytext (including the Properties drawer and all), but leave any subtrees alone. So the end result should look like

* Headline 1
  :PROPERTIES:
  :ID: h1
  :END:
  This is some UPDATED text.

  As you can see, it can
  be multlined.
** Subheading A
   :PROPERTIES:
   :ID: s1
   :END:
   The subheading can also have text.

What's the fastest / easiest way to do this with elisp?

  • delete-region and insert your symbol/text. There are insufficient details in the question be more specific as we do not know how it is that the region to be replaced will be selected/chosen, where the cursor is in the buffer, whether the region is presently selected/highlighted, whether the original poster is looking for a select region consisting of the subtree in relation to wherever the cursor is presently located, etc. – lawlist Sep 16 '16 at 5:11
  • The original poster may also be interested in org-cut-subtree: "Cut the current subtree into the clipboard. With prefix arg N, cut this many sequential subtrees. This is a short-hand for marking the subtree and then cutting it." – lawlist Sep 16 '16 at 5:33
1

You can use my org-parser library to pull the org file as a data structure, then make the changes, then write back to the org file:

(let* ((parsed-buffer (org-parser-parse-buffer "test.org"))
       (block-to-change (first parsed-buffer))
       (mytext ":PROPERTIES:
:ID: h1
:END:
This is some UPDATED text.

As you can see, it can
be multlined.")
       (mytext-as-org-block (concat "* title to be ignored\n" mytext))
       (new-properties (org-parser--get-properties mytext-as-org-block))
       (new-body (org-parser--get-body mytext-as-org-block)))
  (puthash :properties new-properties block-to-change)
  (puthash :body new-body block-to-change)
  (with-current-buffer "test.org"
    (erase-buffer)
    (insert (org-parser--to-string parsed-buffer))))

You obviously won't need to include mytext as part of the let*, and in fact, if you have the properties block as an alist ('(("ID" . "h1"))) separate from the body text, you can skip the mytext-as-org-block, and jump directly to the puthash lines.

  • What advantages does your library have over Org mode's parser, org-element-parse-buffer? – Omar Aug 12 '17 at 18:21
  • It more abstracts away the underlying file into a data structure, without reference back to the original string, nor extra attributes (why does each list from #'org-element-parse-buffer have org-data nil at the beginning of it?). This data structure is far more intuitive to work with. (Compare a plain list with only two items with both #'org-element-parse-buffer and #org-parser-parse-buffer). – zck Aug 13 '17 at 6:04
0

This plain replace-regexp seems to cut it:

(defun org-replace-h1 ()
  (interactive)
  (let ((mytext "
  :PROPERTIES:
  :ID: h1
  :END:
  This is some UPDATED text.

  As you can see, it can
  be multlined."))

    (goto-char (point-min))
    (while (search-forward-regexp "^\\* \\(.+\\)\n\\(.+\\|\n\\)+?\\*" nil t)
      (replace-match (concat "* " (match-string 1) mytext "\n*") t nil))))

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.