我该如何走组织模式树?


10

背景

我正在为Emacs编写演示模式。我希望输入的是组织文件,因为组织文件非常适合数据。

问题

我必须将组织模式文件转换为可以迭代的“幻灯片”数据结构列表。为此,我想使用以下组织模式文件:

* 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问题

我当然对将组织模式文件转换为Elisp数据结构的其他方式持开放态度。我认为 org-export对我来说不是合适的工具,因为我不想最终得到一个包含结果的新文件,但可以迭代一个数据结构。我的幼稚方式就像“给我所有顶级标题,然后我可以获取它们的属性和包含的元素(例如,纯文本或嵌套列表-无论是其他标题还是破折号列表)”。


2
我相信第三个可选参数no-recursionorg-element-map应该做你想要什么。
wvxvw

2
转到文件底部,然后向后搜索标题,抓取所有内容,然后继续进行-重复此过程-直到到达文件顶部,然后完成处理,怎么样?我们后退是因为每次搜索后点已经在标题的开头,所以它比前进然后返回到标题的开头要更有效。这是组织议程的工作方式-即,组织议程列表,组织搜索视图,组织标签视图。
法律名单

Answers:


7

我有一个类似的问题,所以也许会有所帮助-我对org导出或org内部结构不是很熟悉,但是我找不到将org文件解析为树形结构的任何东西。但是给定一个像

* england
** london
** bristol
* france

它会给你

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

并且还可以包含树中的其他信息。


因此,给定一个简单的级别列表,我们需要生成一棵树,例如(1 1 2 3 1)=>(1 1(2(3))1)。我也找不到能执行此操作的函数,因此在多次绘制cons单元后写了一个-我确定有更好的方法可以执行此操作,但是它可以工作。该函数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")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.