Como posso andar em uma árvore de modo organizacional?


10

fundo

Estou escrevendo um modo de apresentação para o Emacs. Eu gostaria que a entrada fosse arquivos organizacionais, pois eles são ótimos para dados.

Problema

Eu tenho que converter o arquivo de modo organizacional em uma lista de estruturas de dados "slide" pelas quais posso iterar. Para fazer isso, gostaria de usar algo como o seguinte arquivo de modo organizacional:

* 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

e ser capaz de caminhar. Eu tentei (org-element-parse-buffer), e isso me dá uma lista de elementos, mas é difícil descobrir como caminhar mais neles. Por exemplo, chamar (org-element-map (org-element-parse-buffer) 'headline #'identity)fornece uma lista de três elementos; o último representa o "título aninhado". Quero que "título aninhado" seja filho de "este é o segundo título, com conteúdo".

Evitando o problema XY

Certamente, estou aberto a outras maneiras de converter um arquivo de modo organizacional em uma estrutura de dados Elisp. Não acho que a exportação orgânica seja a ferramenta certa para mim, porque não quero terminar com um novo arquivo que contenha os resultados, mas com uma estrutura de dados que eu possa percorrer. Meu jeito ingênuo é algo como "me dê todas as manchetes de nível superior, e então eu posso obter suas propriedades e elementos contidos (por exemplo, texto simples ou listas aninhadas - sejam manchetes adicionais ou listas de traços)".


2
Acredito que o terceiro argumento opcional no-recursionde org-element-mapfaça o que você deseja.
Wvxvw

2
Que tal ir até a parte inferior do arquivo e procurar um título para trás, pegar tudo e continuar - repetindo o processo - até chegar ao topo do arquivo e pronto? Retrocedemos porque o ponto já está no início do cabeçalho após cada pesquisa, por isso é mais eficiente do que avançar e depois voltar um pouco para estar no início do cabeçalho. É assim que a agenda organizacional funciona - ou seja, lista de agenda organizacional, exibição de pesquisa de org, exibição de tags de org.
lawlist

Respostas:


7

Eu tive um problema semelhante, então talvez isso ajude - não estou muito familiarizado com a exportação organizacional ou com a organização interna, mas não consegui encontrar nada que analise um arquivo organizacional em uma estrutura de árvore. Mas dado um buffer como

* england
** london
** bristol
* france

vai te dar

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

e pode incluir outras informações da árvore também.


Portanto, dada uma lista plana de níveis, precisamos produzir uma árvore, por exemplo (1 1 2 3 1) => (1 1 (2 (3)) 1). Eu não consegui encontrar uma função que faria isso, então escrevi uma após muito desenho das células contras - tenho certeza de que há uma maneira melhor de fazer isso, mas funciona. A função unflattenutiliza uma lista simples e algumas funções para extrair as informações desejadas da lista e dos níveis do item e produz uma estrutura em árvore.

Em org-get-header-listvocê pode adicionar mais informações você deseja extrair de cada artigo com chamadas para org-element-property, em seguida, em org-get-header-treeque você pode incluir funções para extrair as informações da lista.

Tal como está, isso não inclui a manipulação de listas de traços, mas talvez possa ser adaptado para lidar com aquelas também sem muitos 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")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.