Bagaimana saya bisa berjalan pohon mode-org?

10

Latar Belakang

Saya sedang menulis mode presentasi untuk Emacs. Saya ingin input menjadi file org, karena file org bagus untuk data.

Masalah

Saya harus mengubah file mode org ke dalam daftar "slide" struktur data yang bisa saya ulangi. Untuk melakukan ini, saya ingin mengambil sesuatu seperti file mode-org berikut:

* 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

dan bisa berjalan itu. Saya sudah mencoba (org-element-parse-buffer), dan itu memang memberi saya daftar elemen, tetapi sulit untuk mencari cara untuk melangkah lebih jauh di dalamnya. Misalnya, panggilan (org-element-map (org-element-parse-buffer) 'headline #'identity)memberi daftar tiga elemen; yang terakhir mewakili "headline bersarang". Saya ingin "nested headline" menjadi anak dari "this headline kedua, dengan konten".

Menghindari masalah XY

Saya tentu saja terbuka dengan cara lain untuk mengubah file mode-org menjadi struktur data Elisp. Saya tidak berpikir ekspor-org adalah alat yang tepat bagi saya, karena saya tidak ingin berakhir dengan file baru yang berisi hasil, tetapi struktur data yang dapat saya ulas. Cara naif saya adalah sesuatu seperti "beri saya semua tajuk top-level, dan kemudian saya bisa mendapatkan properti mereka dan elemen yang terkandung (misalnya, teks biasa atau daftar bersarang - apakah tajuk lebih lanjut atau daftar tanda hubung)".

kawan
sumber
2
Saya percaya argumen opsional ketiga no-recursiondari org-element-mapharus melakukan apa yang Anda inginkan.
wvxvw
2
Bagaimana dengan pergi ke bagian bawah file dan kemudian mencari ke belakang untuk judul, ambil semuanya, dan kemudian terus - ulangi proses - sampai Anda mencapai bagian atas file, lalu lempar selesai? Kami mundur karena titik sudah di awal tajuk setelah setiap pencarian, jadi lebih efisien daripada maju dan kemudian kembali sedikit untuk berada di awal tajuk. Inilah cara kerja org-agenda - yaitu, daftar-agenda-org, tampilan-org-pencarian, tampilan-tag-org.
hukum

Jawaban:

7

Saya memiliki masalah yang sama, jadi mungkin ini akan membantu - Saya tidak terlalu mengenal ekspor org atau internal org, tetapi saya tidak dapat menemukan apa pun yang akan mengurai file org ke struktur pohon. Tapi diberi buffer like

* england
** london
** bristol
* france

itu akan memberi Anda

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

dan dapat memasukkan informasi lain dari pohon juga.


Jadi dengan memberikan daftar level yang datar kita perlu menghasilkan pohon, mis. (1 1 2 3 1) => (1 1 (2 (3)) 1). Saya tidak dapat menemukan fungsi yang akan melakukan ini, jadi tulis satu demi satu gambar banyak sel kontra - saya yakin ada cara yang lebih baik untuk melakukan ini tetapi berfungsi. Fungsi ini unflattenmengambil daftar datar dan beberapa fungsi untuk mengekstrak informasi yang Anda inginkan dari daftar dan tingkat item dan menghasilkan struktur pohon.

Di org-get-header-listAnda dapat menambahkan lebih banyak informasi yang ingin Anda ekstrak dari setiap item dengan panggilan org-element-property, dan kemudian di org-get-header-treeAnda bisa memasukkan fungsi untuk mengekstrak informasi dari daftar.

Seperti berdiri ini tidak termasuk penanganan untuk daftar dasbor, tapi mungkin bisa disesuaikan untuk menangani mereka juga tanpa terlalu banyak masalah ...


(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
sumber