Як я можу ходити по дереву в режимі org?


10

Фон

Я пишу режим презентації для Emacs. Я б хотів, щоб вхідні файли були org файлами, оскільки org файли чудово підходять для даних.

Проблема

Я маю конвертувати файл режиму 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

і вміти ходити по ньому. Я спробував (org-element-parse-buffer), і це дає мені список елементів, але важко зрозуміти, як пройти далі в них. Наприклад, виклик (org-element-map (org-element-parse-buffer) 'headline #'identity)дає список з трьох елементів; останній представляє "вкладений заголовок". Я хочу, щоб "вкладений заголовок" був дочірньою "це другий заголовок із вмістом".

Уникнення проблеми XY

Я, безумовно, відкритий для інших способів перетворення файлу org-mode в структуру даних Elisp. Я не думаю, що org-export - це правильний інструмент для мене, тому що я не хочу закінчувати новим файлом, що містить результати, але структуру даних, яку я можу повторити. Мій наївний спосіб - це щось на кшталт "дайте мені всі заголовки верхнього рівня, і тоді я можу отримати їх властивості та вміщені елементи (наприклад, звичайний текст чи вкладені списки - будь то подальші заголовки чи списки тире)".


2
Я вважаю , що третій додатковий аргумент no-recursionв org-element-mapповинен робити те , що ви хочете.
wvxvw

2
Як щодо того, щоб перейти до нижньої частини файлу, а потім пошукати заголовок назад, захопити все, а потім продовжувати - повторюючи процес - поки не досягнете верхньої частини файлу, а потім киньте готово? Ми повертаємось назад, тому що точка знаходиться вже на початку заголовка після кожного пошуку, тому це більш ефективно, ніж йти вперед, а потім трохи повернутися назад, щоб бути на початку заголовка. Ось так працює org-порядок денний - тобто, org-age-list, org-search-view, org-tags-view.
законник

Відповіді:


7

У мене була подібна проблема, тому, можливо, це допоможе - я не дуже знайомий із експортом org чи внутрішнім органом, але я не зміг знайти нічого, що б розібрало файл org із структурою дерева. Але дано буфер типу

* england
** london
** bristol
* france

це дасть тобі

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

і може включати й іншу інформацію з дерева.


Отже, з урахуванням плоского списку рівнів нам потрібно створити дерево, наприклад (1 1 2 3 1) => (1 1 (2 (3)) 1). Я не зміг знайти функцію, яка б це зробила, і так написав після багаточисленних малюнків мінусних комірок - я впевнений, що є кращий спосіб зробити це, але це працює. Функція unflattenзаймає плоский список і пару функцій для отримання потрібної інформації зі списку та рівнів елементів та створює структуру дерева.

У org-get-header-listможна додати додаткову інформацію ви хочете отримати з кожного пункту з викликами org-element-property, а потім в org-get-header-treeвас можете включати в себе функцію для вилучення інформації зі списку.

Окрім цього, це не включає обробку списків тире, але, можливо, воно може бути адаптоване для обробки також без особливих проблем ...


(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")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.