Làm thế nào tôi có thể đi bộ cây chế độ org?


10

Lý lịch

Tôi đang viết một chế độ trình bày cho Emacs. Tôi muốn đầu vào là tệp org, vì tệp org rất tốt cho dữ liệu.

Vấn đề

Tôi phải chuyển đổi tệp chế độ org thành một danh sách các cấu trúc dữ liệu "trượt" mà tôi có thể lặp qua. Để làm điều này, tôi muốn lấy một cái gì đó giống như tệp chế độ tổ chức sau:

* 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

và có thể đi bộ nó. Tôi đã thử (org-element-parse-buffer), và nó cho tôi một danh sách các yếu tố, nhưng thật khó để tìm ra cách đi xa hơn trong chúng. Ví dụ, gọi (org-element-map (org-element-parse-buffer) 'headline #'identity)cho một danh sách ba yếu tố; cái cuối cùng đại diện cho "tiêu đề lồng nhau". Tôi muốn "tiêu đề lồng nhau" là một đứa trẻ của "đây là tiêu đề thứ hai, có nội dung".

Tránh vấn đề XY

Tôi chắc chắn mở cho các cách khác để chuyển đổi tệp chế độ org thành cấu trúc dữ liệu Elisp. Tôi không nghĩ org-export là công cụ phù hợp với tôi, vì tôi không muốn kết thúc với một tệp mới chứa kết quả, nhưng cấu trúc dữ liệu tôi có thể lặp lại. Cách ngây thơ của tôi là một cái gì đó như "đưa cho tôi tất cả các tiêu đề cấp cao nhất, sau đó tôi có thể lấy các thuộc tính của chúng và chứa các thành phần (ví dụ: văn bản đơn giản hoặc danh sách lồng nhau - cho dù các tiêu đề tiếp theo hoặc danh sách gạch ngang)".


2
Tôi tin rằng các đối số tùy chọn thứ ba no-recursioncủa org-element-mapnên làm những gì bạn muốn.
wvxvw

2
Làm thế nào về việc đi đến dưới cùng của tệp và sau đó tìm kiếm ngược lại cho một tiêu đề, lấy tất cả mọi thứ, và sau đó tiếp tục - lặp lại quá trình - cho đến khi bạn đạt đến đỉnh của tệp, sau đó ném xong? Chúng tôi đi ngược lại vì điểm đã ở đầu tiêu đề sau mỗi lần tìm kiếm, vì vậy nó hiệu quả hơn là đi tiếp và sau đó quay lại một chút để ở đầu tiêu đề. Đây là cách org-agenda hoạt động - tức là org-agenda-list, org-search-view, org-tags-view.
luật

Câu trả lời:


7

Tôi đã gặp một vấn đề tương tự, vì vậy có lẽ điều này sẽ giúp ích - tôi không rành lắm về org export hoặc org internals, nhưng tôi không thể tìm thấy bất cứ điều gì có thể phân tích tệp org thành cấu trúc cây. Nhưng được cung cấp một bộ đệm như

* england
** london
** bristol
* france

nó sẽ cho bạn

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

và có thể bao gồm các thông tin khác từ cây là tốt.


Vì vậy, đưa ra một danh sách các cấp độ phẳng, chúng ta cần tạo ra một cây, ví dụ (1 1 2 3 1) => (1 1 (2 (3)) 1). Tôi không thể tìm thấy một chức năng nào có thể làm được điều này vì vậy đã viết một cái sau khi vẽ nhiều ô khuyết điểm - tôi chắc chắn có cách tốt hơn để làm điều này nhưng nó hoạt động. Hàm unflattenlấy một danh sách phẳng và một vài hàm để trích xuất thông tin bạn muốn từ danh sách và các mức vật phẩm và tạo ra cấu trúc cây.

Trong org-get-header-listbạn có thể thêm nhiều thông tin bạn muốn trích xuất từ ​​mỗi mục bằng các cuộc gọi đến org-element-property, và sau đó trong org-get-header-treebạn có thể bao gồm các chức năng để trích xuất thông tin từ danh sách.

Vì nó không bao gồm việc xử lý danh sách gạch ngang, nhưng có lẽ nó có thể được điều chỉnh để xử lý những danh sách đó mà không gặp quá nhiều rắc rối ...


(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")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.