Come posso camminare su un albero in modalità org?


10

sfondo

Sto scrivendo una modalità di presentazione per Emacs. Vorrei che l'input fosse un file org, poiché i file org sono ottimi per i dati.

Problema

Devo convertire il file in modalità org in un elenco di strutture di dati "slide" che posso scorrere. Per fare questo, vorrei prendere qualcosa come il seguente file in modalità org:

* 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

ed essere in grado di percorrerlo. Ci ho provato (org-element-parse-buffer), e questo mi dà un elenco di elementi, ma è difficile capire come andare oltre. Ad esempio, chiamando (org-element-map (org-element-parse-buffer) 'headline #'identity)fornisce un elenco di tre elementi; l'ultimo rappresenta il "titolo nidificato". Voglio che "titolo nidificato" sia figlio di "questo è il secondo titolo, con i contenuti".

Evitare il problema XY

Sono certamente aperto ad altri modi di convertire un file in modalità org in una struttura di dati Elisp. Non penso che org-export sia lo strumento giusto per me, perché non voglio finire con un nuovo file contenente i risultati, ma una struttura di dati che posso scorrere. Il mio modo ingenuo è qualcosa come "dammi tutti i titoli di livello superiore, e quindi posso ottenere le loro proprietà e gli elementi contenuti (ad esempio, testo semplice o elenchi nidificati - che siano ulteriori titoli o elenchi di trattini)".


2
Credo che il terzo argomento facoltativo no-recursiondi org-element-mapdovrebbe fare quello che vuoi.
wvxvw,

2
Che ne dici di andare in fondo al file e quindi cercare all'indietro un'intestazione, afferrare tutto e quindi continuare - ripetendo il processo - fino a raggiungere la cima del file, quindi lanciare fatto? Andiamo indietro perché il punto è già all'inizio dell'intestazione dopo ogni ricerca, quindi è più efficiente che andare avanti e poi tornare un po 'indietro all'inizio dell'intestazione. Ecco come funziona l'org-agenda: ad esempio, elenco-agenda-org, vista-ricerca-org, vista-tag-org.
elenco delle leggi

Risposte:


7

Ho avuto un problema simile, quindi forse questo mi aiuterà - non ho molta familiarità con l'esportazione dell'org o interni dell'org, ma non sono riuscito a trovare nulla che possa analizzare un file org in una struttura ad albero. Ma dato un buffer simile

* england
** london
** bristol
* france

ti darà

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

e può includere anche altre informazioni dall'albero.


Quindi, dato un elenco semplice di livelli, dobbiamo produrre un albero, ad es. (1 1 2 3 1) => (1 1 (2 (3)) 1). Non sono riuscito a trovare una funzione che lo farebbe, così ne ho scritto uno dopo aver disegnato molte celle contro - sono sicuro che c'è un modo migliore per farlo ma funziona. La funzione unflattenaccetta un elenco semplice e un paio di funzioni per estrarre le informazioni desiderate dall'elenco e dai livelli degli elementi e produce una struttura ad albero.

In org-get-header-listsi potrebbe aggiungere ulteriori informazioni che si desidera estrarre da ogni voce con chiamate a org-element-property, e poi in org-get-header-treesi potrebbe includere funzioni per estrarre le informazioni dalla lista.

Allo stato attuale ciò non include la gestione delle dashboard, ma forse potrebbe essere adattato per gestirle anche senza troppi problemi ...


(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")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.