11
votes

I want to generate strings containing Org-mode text without actually hard-coding the syntax. For example i want to run a function such as (org-generate (org-generate-heading "heading" (org-generate-plain-list '("foo" "bar" "baz"))) and it will return:

* heading
  - foo
  - bar
  - baz

In other words, i want to create Org-mode documents of arbitrary complexity without micromanaging syntactic features like asterisks and indentation, only via calling functions with parameters, that return some Org objects. Is it possible to do that? Maybe via org-element?

1
What are you trying to do? Your example doesn't say anything about context.abo-abo
I would recommend looking at org-capture-templates, because it seems very similar to what you want to todo.lawlist
org-element-interpret-data could probably be your org-generate, but the data seems to need a lot of details about positions, levels, bullets etc. so org-generate-heading and friends would still need to be written and would be nontrivial.Michał Politowski

1 Answers

4
votes

INITIAL (March 14, 2014):  First rough draft.

EDIT (March 15, 2014):  Created and revised the function named example. The path of the let-bound variable org-file must coincide with an existing org-mode file. The let-bound variables main-heading and sub-heading are not being used at this time due to an apparent limitation with using variables in a list that begins with '( -- i.e., those two variables are not recognized under this circumstance. The function org-capture from org-capture.el has been modified to include the contents of the function org-capture-set-plist, which in turn has been modified to eliminate the first two elements of org-capture-entry (aka org-capture-templates) -- both entries (i.e., :key and :description) are for manually selecting a template from the user-interface, and are not needed when generating an org buffer programmatically as is being done with this example. In addition, the portions of the function org-capture relating to manually selecting a template have been removed.

EDIT (March 16, 2014):  Revised variables and list handling based upon lessons provided by @sds and @lunaryorn in the following thread: https://stackoverflow.com/a/22440518/2112489  Added four optional incoming variables -- (1) main-heading; (2) sub-heading-headline; (3) sub-heading-complete; and (4) plain-list. The example now work either interactively, or by evaluating the function using the following format:  (org-generate "PROJECT" "Thesis" "** Thesis\n:PROPERTIES:\n:END:" '("a" "b" "c"))

EDIT (March 19, 2014):  org-generate is now a non-interactive function that requires incoming variables -- the doc-string has been updated. Created a function named example that utilizes the new format for org-generate.


;; EXAMPLES:
;; (org-generate 'entry "/Users/HOME/Desktop/myproject.org" "PROJECT" "Thesis" "** Thesis\n   :PROPERTIES:\n   :END:")
;; (org-generate 'item "/Users/HOME/Desktop/myproject.org" "PROJECT" "Thesis" nil '("a" "b" "c"))

(defun example ()
(interactive)
  (let* (
      (org-file "/Users/HOME/Desktop/myproject.org")
      (level-one "TASKS")
      (level-two
        "Active [\#A] Generate Org-mode objects programmatically.")
      (full-level-two (concat
        "** Active [\#A] Generate Org-mode objects programmatically.\n"
        "   DEADLINE: <%<%Y-%m-%d %a>>\n"
        "   :PROPERTIES:\n"
        "   :ToodledoFolder: TASKS\n"
        "   :END:"))
       (plain-list '("foo" "bar" "baz")) )
    (org-generate 'entry org-file level-one level-two full-level-two)
    (org-generate 'item org-file level-one level-two nil plain-list) ))

(defun org-generate (type org-file level-one
  &optional level-two full-level-two plain-list)
"Formating options for `org-capture-entry` are similar to `org-capture-templates`.
However, the first two elements (i.e., `:key` and `:description`) are NOT used.
Please see the doc-string of the variable `org-capture-templates` for more info.
  (1) `type`:  required -- 'entry | 'item
  (2) `org-file`:  required -- path to the org-mode file.
  (3) `level-one`:  required -- main heading.
  (4) `level-two`:  optional -- sub-heading headline (only).
  (5) `full-level-two`:  optional -- complete sub-heading.
  (6) `plain-list`:  optional -- a list.
EXAMPLES:
  `(org-generate 'entry org-file level-one level-two full-level-two)`
  `(org-generate 'item org-file level-one level-two nil plain-list)` "
  (require 'org-capture)
  (let (org-capture-entry)
    (cond
      ((eq type 'entry)
        (setq org-capture-entry
          `(entry
            (file+headline ,org-file ,level-one)
              ,full-level-two :empty-lines 1 :immediate-finish t))
        (lawlist-org-capture))
      ((eq type 'item)
        (setq org-capture-entry
          `(item
            (file+olp ,org-file ,level-one ,level-two)
              nil :empty-lines 1 :immediate-finish t))
        (mapcar (lambda (x)
          (progn
            (setcar (nthcdr 2 org-capture-entry) x)
            (lawlist-org-capture) ))
          plain-list)))))

(defun lawlist-org-capture ()
    (let* ((orig-buf (current-buffer))
     (annotation (if (and (boundp 'org-capture-link-is-already-stored)
        org-capture-link-is-already-stored)
         (plist-get org-store-link-plist :annotation)
       (ignore-errors (org-store-link nil))))
     (entry org-capture-entry)
     initial)
      (setq initial (or org-capture-initial
      (and (org-region-active-p)
           (buffer-substring (point) (mark)))))
      (when (stringp initial)
  (remove-text-properties 0 (length initial) '(read-only t) initial))
      (when (stringp annotation)
  (remove-text-properties 0 (length annotation)
        '(read-only t) annotation))
  (setq org-capture-plist (copy-sequence (nthcdr 3 entry)))
  (org-capture-put :target (nth 1 entry))
  (let ((txt (nth 2 entry)) (type (or (nth 0 entry) 'entry)))
    (when (or (not txt) (and (stringp txt) (not (string-match "\\S-" txt))))
      (cond
       ((eq type 'item) (setq txt "- %?"))
       ((eq type 'checkitem) (setq txt "- [ ] %?"))
       ((eq type 'table-line) (setq txt "| %? |"))
       ((member type '(nil entry)) (setq txt "* %?\n  %a"))))
    (org-capture-put :template txt :type type))
  (org-capture-get-template)
  (org-capture-put :original-buffer orig-buf
       :original-file (or (buffer-file-name orig-buf)
              (and (featurep 'dired)
             (car (rassq orig-buf
                   dired-buffers))))
       :original-file-nondirectory
       (and (buffer-file-name orig-buf)
            (file-name-nondirectory
             (buffer-file-name orig-buf)))
       :annotation annotation
       :initial initial
       :return-to-wconf (current-window-configuration)
       :default-time
       (or org-overriding-default-time
           (org-current-time)))
  (org-capture-set-target-location)
  (condition-case error
      (org-capture-put :template (org-capture-fill-template))
    ((error quit)
     (if (get-buffer "*Capture*") (kill-buffer "*Capture*"))
     (error "Capture abort: %s" error)))
  (setq org-capture-clock-keep (org-capture-get :clock-keep))
    (condition-case error
        (org-capture-place-template
         (equal (car (org-capture-get :target)) 'function))
      ((error quit)
       (if (and (buffer-base-buffer (current-buffer))
          (string-match "\\`CAPTURE-" (buffer-name)))
     (kill-buffer (current-buffer)))
       (set-window-configuration (org-capture-get :return-to-wconf))
       (error "Error.")))
    (if (and (derived-mode-p 'org-mode)
       (org-capture-get :clock-in))
        (condition-case nil
      (progn
        (if (org-clock-is-active)
      (org-capture-put :interrupted-clock
           (copy-marker org-clock-marker)))
        (org-clock-in)
        (org-set-local 'org-capture-clock-was-started t))
    (error
     "Could not start the clock in this capture buffer")))
    (if (org-capture-get :immediate-finish)
        (org-capture-finalize))))

Example
(source: lawlist.com)