¿Cómo puedo recorrer un árbol de modo org?


10

Antecedentes

Estoy escribiendo un modo de presentación para Emacs. Me gustaría que la entrada sea archivos de organización, ya que los archivos de organización son excelentes para los datos.

Problema

Tengo que convertir el archivo de modo de organización en una lista de estructuras de datos "deslizantes" a través de las cuales puedo iterar. Para hacer esto, me gustaría tomar algo como el siguiente archivo de modo 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

y poder caminarlo. Lo he intentado (org-element-parse-buffer), y eso me da una lista de elementos, pero es difícil descubrir cómo avanzar más en ellos. Por ejemplo, llamar (org-element-map (org-element-parse-buffer) 'headline #'identity)proporciona una lista de tres elementos; el último representa el "título anidado". Quiero que "título anidado" sea hijo de "este es el segundo título, con contenido".

Evitar el problema XY

Ciertamente estoy abierto a otras formas de convertir un archivo de modo org en una estructura de datos de Elisp. No creo que org-export sea la herramienta adecuada para mí, porque no quiero terminar con un nuevo archivo que contenga los resultados, sino una estructura de datos a través de la cual pueda iterar. Mi manera ingenua es algo así como "darme todos los titulares de nivel superior, y luego puedo obtener sus propiedades y elementos contenidos (por ejemplo, texto plano o listas anidadas, ya sean titulares o listas de guiones)".


2
Creo que el tercer argumento opcional no-recursionde org-element-mapdebería hacer lo que quieras.
wvxvw

2
¿Qué tal ir al final del archivo y luego buscar hacia atrás un encabezado, tomar todo y luego continuar, repitiendo el proceso, hasta llegar a la parte superior del archivo, y luego listo? Retrocedemos porque el punto ya está al comienzo del encabezado después de cada búsqueda, por lo que es más eficiente que avanzar y luego retroceder un poco para estar al comienzo del encabezado. Así es como funciona org-agenda, es decir, org-agenda-list, org-search-view, org-tags-view.
ley

Respuestas:


7

Tuve un problema similar, por lo que tal vez esto ayude: no estoy muy familiarizado con la exportación de org o los componentes internos de la organización, pero no pude encontrar nada que analizara un archivo de organización en una estructura de árbol. Pero dado un búfer como

* england
** london
** bristol
* france

te dará

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

y puede incluir otra información del árbol también.


Entonces, dada una lista plana de niveles, necesitamos producir un árbol, por ejemplo (1 1 2 3 1) => (1 1 (2 (3)) 1). No pude encontrar una función que hiciera esto, así que escribí una después de dibujar muchas celdas en contra. Estoy seguro de que hay una mejor manera de hacerlo, pero funciona. La función unflattentoma una lista plana y un par de funciones para extraer la información que desea de la lista y los niveles de elementos y produce una estructura de árbol.

En org-get-header-listpodría agregar más información que desea extraer de cada elemento con llamadas org-element-property, y luego org-get-header-treepodría incluir funciones para extraer la información de la lista.

Tal como está, esto no incluye el manejo de listas de guiones, pero tal vez podría adaptarse para manejarlas también sin demasiados problemas ...


(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")
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.