Comment puis-je parcourir une arborescence en mode organisation?

10

Contexte

J'écris un mode de présentation pour Emacs. J'aimerais que l'entrée soit des fichiers org, car les fichiers org sont parfaits pour les données.

Problème

Je dois convertir le fichier en mode org en une liste de structures de données «diapositives» que je peux parcourir. Pour ce faire, je voudrais prendre quelque chose comme le fichier org-mode suivant:

* 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

et être capable de marcher. J'ai essayé (org-element-parse-buffer), et cela me donne une liste d'éléments, mais il est difficile de comprendre comment y aller plus loin. Par exemple, appeler (org-element-map (org-element-parse-buffer) 'headline #'identity)donne une liste de trois éléments; le dernier représente le "titre imbriqué". Je veux que "titre imbriqué" soit un enfant de "c'est le deuxième titre, avec le contenu".

Éviter le problème XY

Je suis certainement ouvert à d'autres façons de convertir un fichier en mode organisationnel en une structure de données Elisp. Je ne pense pas que org-export soit le bon outil pour moi, car je ne veux pas me retrouver avec un nouveau fichier contenant les résultats, mais une structure de données que je peux parcourir. Ma façon naïve est quelque chose comme «donnez-moi tous les titres de haut niveau, puis je peux obtenir leurs propriétés et éléments contenus (par exemple, du texte brut ou des listes imbriquées - que ce soit d'autres titres ou des listes de tirets)».

zck
la source
2
Je crois que le troisième argument optionnel no-recursionde org-element-mapdevrait faire ce que vous voulez.
wvxvw
2
Que diriez-vous d'aller au bas du fichier, puis de rechercher une rubrique en arrière, de tout saisir, puis de continuer - en répétant le processus - jusqu'à ce que vous atteigniez le début du fichier, puis lancez? Nous allons en arrière parce que le point est déjà au début de l'en-tête après chaque recherche, donc c'est plus efficace que d'aller en avant puis de reculer un peu pour être au début de l'en-tête. Voici comment fonctionne org-agenda - c'est-à-dire org-agenda-list, org-search-view, org-tags-view.
lawlist

Réponses:

7

J'ai eu un problème similaire, alors peut-être que cela aidera - je ne suis pas très familier avec l'exportation ou les org internes, mais je n'ai rien trouvé qui pourrait analyser un fichier org dans une arborescence. Mais étant donné un tampon comme

* england
** london
** bristol
* france

ça vous donnera

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

et peut également inclure d'autres informations de l'arbre.


Donc, étant donné une liste plate de niveaux, nous devons produire un arbre, par exemple (1 1 2 3 1) => (1 1 (2 (3)) 1). Je ne pouvais pas non plus trouver une fonction qui ferait cela, alors j'en ai écrit une après beaucoup de dessin de cellules contre - je suis sûr qu'il y a une meilleure façon de le faire mais cela fonctionne. La fonction unflattenprend une liste plate et quelques fonctions pour extraire les informations souhaitées de la liste et des niveaux d'élément et produit une arborescence.

Dans org-get-header-listvous pouvez ajouter plus d'informations que vous souhaitez extraire de chaque élément avec des appels à org-element-property, puis org-get-header-treevous pouvez inclure des fonctions pour extraire les informations de la liste.

En l'état, cela n'inclut pas la gestion des listes de tirets, mais peut-être pourrait-il être adapté pour gérer celles-ci également sans trop de problèmes ...


(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
la source