Wie kann ich einen Org-Modus-Baum durchlaufen?

10

Hintergrund

Ich schreibe einen Präsentationsmodus für Emacs. Ich möchte, dass die Eingabe Organisationsdateien sind, da Organisationsdateien sich hervorragend für Daten eignen.

Problem

Ich muss die Organisationsmodusdatei in eine Liste von "Folien" -Datenstrukturen konvertieren, die ich durchlaufen kann. Dazu möchte ich so etwas wie die folgende Org-Modus-Datei nehmen:

* this is the first headline, with a title property and no contents
* this is the second headline, with contents
- dash list nested under the second headline
  - further nested
** nested headline

und in der Lage sein, es zu gehen. Ich habe es versucht (org-element-parse-buffer), und das gibt mir eine Liste von Elementen, aber es ist schwer herauszufinden, wie ich weiter darin gehen kann. Wenn Sie beispielsweise aufrufen, erhalten Sie (org-element-map (org-element-parse-buffer) 'headline #'identity)eine Liste mit drei Elementen. Die letzte steht für die "verschachtelte Überschrift". Ich möchte, dass "verschachtelte Überschrift" ein Kind von "Dies ist die zweite Überschrift mit Inhalt" ist.

Vermeiden des XY-Problems

Ich bin auf jeden Fall offen für andere Möglichkeiten, eine Org-Modus-Datei in eine Elisp-Datenstruktur zu konvertieren. Ich denke nicht, dass org-export das richtige Werkzeug für mich ist, da ich nicht mit einer neuen Datei enden möchte, die die Ergebnisse enthält, sondern mit einer Datenstruktur, die ich durchlaufen kann. Mein naiver Weg ist so etwas wie "Gib mir alle Überschriften der obersten Ebene, und dann kann ich ihre Eigenschaften und enthaltenen Elemente (z. B. Klartext oder verschachtelte Listen - ob weitere Überschriften oder Strichlisten) abrufen".

zck
quelle
2
Ich glaube, das dritte optionale Argument no-recursionvon org-element-mapsollte tun, was Sie wollen.
wvxvw
2
Wie wäre es, wenn Sie zum Ende der Datei gehen und dann rückwärts nach einer Überschrift suchen, alles greifen und dann weitermachen - den Vorgang wiederholen - bis Sie den Anfang der Datei erreichen und dann fertig werfen? Wir gehen rückwärts, weil der Punkt nach jeder Suche bereits am Anfang der Überschrift steht. Dies ist also effizienter, als vorwärts zu gehen und dann etwas zurück zu gehen, um am Anfang der Überschrift zu stehen. So funktioniert die Org-Agenda - dh Org-Agenda-Liste, Org-Suchansicht, Org-Tags-Ansicht.
Lawlist

Antworten:

7

Ich hatte ein ähnliches Problem, daher hilft dies möglicherweise - ich bin nicht sehr vertraut mit dem Export von Organisationen oder den Interna von Organisationen, aber ich konnte nichts finden, das eine Organisationsdatei in eine Baumstruktur analysieren könnte. Aber gegeben ein Puffer wie

* england
** london
** bristol
* france

es wird dir geben

(org-get-header-tree) => ("england" ("london" "bristol") "france")

und kann auch andere Informationen aus dem Baum enthalten.


Bei einer flachen Liste von Ebenen müssen wir also einen Baum erzeugen, z. B. (1 1 2 3 1) => (1 1 (2 (3)) 1). Ich konnte auch keine Funktion finden, die dies tun würde, also schrieb ich eine, nachdem ich viele Nachteile gezeichnet hatte - ich bin sicher, es gibt einen besseren Weg, dies zu tun, aber es funktioniert. Die Funktion verwendet unflatteneine flache Liste und einige Funktionen, um die gewünschten Informationen aus der Liste und den Elementebenen zu extrahieren und eine Baumstruktur zu erstellen.

In org-get-header-listSie weitere Informationen hinzufügen können Sie mit Anrufen von jedem Elemente extrahieren org-element-propertyund dann in org-get-header-treeFunktionen , die Sie könnten die Informationen aus der Liste zu extrahieren.

So wie es aussieht, beinhaltet dies keine Behandlung für Dash-Listen, aber vielleicht könnte es angepasst werden, um diese auch ohne allzu große Probleme zu behandeln ...


(defun unflatten (xs &optional fn-value fn-level)
  "Unflatten a list XS into a tree, e.g. (1 2 3 1) => (1 (2 (3)) 1).
FN-VALUE specifies how to extract the values from each element, which
are included in the output tree, FN-LEVEL tells how to extract the
level of each element. By default these are the `identity' function so
it will work on a list of numbers."
  (let* ((level 1)
         (tree (cons nil nil))
         (start tree)
         (stack nil)
         (fn-value (or fn-value #'identity))
         (fn-level (or fn-level #'identity)))
    (dolist (x xs)
      (let ((x-value (funcall fn-value x))
            (x-level (funcall fn-level x)))
        (cond ((> x-level level)
               (setcdr tree (cons (cons x-value nil) nil))
               (setq tree (cdr tree))
               (push tree stack)
               (setq tree (car tree))
               (setq level x-level))
              ((= x-level level)
               (setcdr tree (cons x-value nil))
               (setq tree (cdr tree)))
              ((< x-level level)
               (while (< x-level level)
                 (setq tree (pop stack))
                 (setq level (- level 1)))
               (setcdr tree (cons x-value nil))
               (setq tree (cdr tree))
               (setq level x-level)))))
      (cdr start)))

; eg (unflatten '(1 2 3 2 3 4)) => '(1 (2 (3) 2 (3 (4))))


(defun org-get-header-list (&optional buffer) 
  "Get the headers of an org buffer as a flat list of headers and levels.
Buffer will default to the current buffer."
  (interactive)
  (with-current-buffer (or buffer (current-buffer))
    (let ((tree (org-element-parse-buffer 'headline)))
      (org-element-map 
          tree 
          'headline
        (lambda (el) (list 
                 (org-element-property :raw-value el) ; get header title without tags etc
                 (org-element-property :level el) ; get depth
                 ;; >> could add other properties here
                 ))))))

; eg (org-get-header-list) => (("pok" 1) ("lkm" 1) (("cedar" 2) ("yr" 2)) ("kjn" 1))


(defun org-get-header-tree (&optional buffer)
  "Get the headers of the given org buffer as a tree."
  (interactive)
  (let* ((headers (org-get-header-list buffer))
         (header-tree (unflatten headers  
                 (lambda (hl) (car hl))  ; extract information to include in tree
                 (lambda (hl) (cadr hl)))))  ; extract item level
    header-tree))

; eg (org-get-header-tree) => ("pok" "lkm" ("cedar" "yr") "kjn")
Brian Burns
quelle