Jak mogę przejść po drzewie w trybie organizacji?

10

tło

Piszę tryb prezentacji dla Emacsa. Chciałbym, aby dane wejściowe były plikami org, ponieważ pliki org świetnie nadają się do przechowywania danych.

Problem

Muszę przekonwertować plik trybu org na listę „slajdów” struktur danych, które mogę iterować. Aby to zrobić, chciałbym wziąć coś takiego jak następujący plik trybu 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

i być w stanie chodzić. Próbowałem (org-element-parse-buffer), a to daje mi listę elementów, ale trudno jest wymyślić, jak iść dalej w nich. Na przykład wywołanie (org-element-map (org-element-parse-buffer) 'headline #'identity)daje listę trzech elementów; ostatni reprezentuje „zagnieżdżony nagłówek”. Chcę, aby „zagnieżdżony nagłówek” był dzieckiem „to drugi nagłówek z zawartością”.

Unikanie problemu XY

Z pewnością jestem otwarty na inne sposoby konwertowania pliku w trybie org na strukturę danych Elisp. Nie sądzę, że org-export jest dla mnie właściwym narzędziem, ponieważ nie chcę skończyć z nowym plikiem zawierającym wyniki, ale strukturą danych, którą mogę iterować. Mój naiwny sposób polega na tym, że „podaj mi wszystkie nagłówki najwyższego poziomu, a następnie mogę uzyskać ich właściwości i zawarte elementy (np. Zwykły tekst lub listy zagnieżdżone - niezależnie od tego, czy będą to kolejne nagłówki czy listy myślników)”.

zck
źródło
2
Wierzę, że trzeci opcjonalny argument no-recursionod org-element-mappowinni robić, co chcesz.
wvxvw
2
Co powiesz na przejście do dolnej części pliku, a następnie wyszukiwanie do tyłu nagłówka, chwytanie wszystkiego, a następnie kontynuowanie - powtarzanie procesu - aż dojdziesz do początku pliku, a następnie rzucić gotowe? Cofamy się, ponieważ punkt znajduje się już na początku nagłówka po każdym wyszukiwaniu, więc jest bardziej wydajny niż przejście do przodu, a następnie cofnięcie się nieco na początku nagłówka. Tak działa org-agenda - tj. Org-agenda-list, org-search-view, org-tags-view.
prawnik

Odpowiedzi:

7

Miałem podobny problem, więc może to pomoże - nie znam się dobrze na eksportowaniu org lub wewnętrznych org, ale nie mogłem znaleźć niczego, co analizowałoby plik org na strukturę drzewa. Ale biorąc pod uwagę bufor

* england
** london
** bristol
* france

da ci to

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

i może zawierać także inne informacje z drzewa.


Biorąc pod uwagę płaską listę poziomów, musimy stworzyć drzewo, np. (1 1 2 3 1) => (1 1 (2 (3)) 1). Nie mogłem znaleźć funkcji, która by to zrobiła, więc napisałem ją po wielu rysunkach komórek przeciwnych - jestem pewien, że jest lepszy sposób, ale to działa. Funkcja unflattenpobiera płaską listę i kilka funkcji w celu wyodrębnienia żądanych informacji z listy i poziomów pozycji i tworzy strukturę drzewa.

W org-get-header-listmożesz dodać więcej informacji, które chcesz wyodrębnić z każdego elementu z połączeniami do org-element-property, a następnie org-get-header-treemożesz włączyć funkcje wyodrębnienia informacji z listy.

W obecnej formie nie obejmuje to obsługi list kontrolnych, ale być może można je dostosować do obsługi również bez większych problemów ...


(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
źródło