Como exportar títulos de nível superior do buffer do modo organizacional para arquivos separados?

17

Como cada cabeçalho de nível superior de um org-modebuffer pode ser exportado para um arquivo separado com o nome do valor do CUSTOM_IDtítulo + (higienizado) correspondente ?

Digamos que um buffer contenha:

* Title of Heading 1
  :PROPERTIES:
  :CUSTOM_ID: fibrillogenesis
  :END:
  Suspendisse potenti. Mauris ac felis vel velit tristique imperdiet.  

** Sub-Heading
   Nullam rutrum.

* Another Title for Heading 2
  :PROPERTIES:
  :CUSTOM_ID: mitochondrion
  :END:
  Mauris mollis tincidunt felis.  Sed bibendum.

O resultado final seria um diretório contendo dois arquivos, um para cada um dos títulos de nível superior, com o formato escolhido no momento da exportação (HTML, LaTeX etc.), com os seguintes nomes e conteúdo de arquivo:

  1. Nome do arquivo da primeira posição exportada: fibrillogenesis-title-of-heading-1.[ext]

    Conteúdo exportado, correspondente ao primeiro cabeçalho de nível superior original:

    * Title of Heading 1
      :PROPERTIES:
      :CUSTOM_ID: fibrillogenesis
      :END:
      Suspendisse potenti. Mauris ac felis vel velit tristique imperdiet.  
    
    ** Sub-Heading 
       Nullam rutrum.
    
  2. Nome do arquivo da segunda posição exportada: mitochondrion-another-title-for-heading-2.[ext]

    Conteúdo exportado, correspondente ao segundo cabeçalho de nível superior original:

    * Another Title for Heading 2
    :PROPERTIES:
    :CUSTOM_ID: mitochondrion
    :END:
    Mauris mollis tincidunt felis.  Sed bibendum. 
    

Ficaria muito grato por qualquer dica, direção, pseudocódigo ou (melhor) código real.

gsl
fonte

Respostas:

27

O comando a seguir permite escolher um back-end e exportar cada subárvore de nível superior para um arquivo separado:

(defun org-export-all (backend)
  "Export all subtrees that are *not* tagged with :noexport: to
separate files.

Note that subtrees must have the :EXPORT_FILE_NAME: property set
to a unique value for this to work properly."
  (interactive "sEnter backend: ")
  (let ((fn (cond ((equal backend "html") 'org-html-export-to-html)
                  ((equal backend "latex") 'org-latex-export-to-latex)
                  ((equal backend "pdf") 'org-latex-export-to-pdf))))
    (save-excursion
      (set-mark (point-min))
      (goto-char (point-max))
      (org-map-entries (lambda () (funcall fn nil t)) "-noexport" 'region-start-level))))

Atualmente, ele suporta exportação HTML ( html), LaTeX ( latex) e PDF ( pdf). Você pode adicionar suporte para mais back-ends adicionando mais cláusulas a cond.

Como a docstring diz, para cada subárvore, você precisa definir a :EXPORT_FILE_NAME:propriedade com o nome do arquivo para o qual deseja exportar. (Veja abaixo outras opções.)

Gerando automaticamente o nome do arquivo de exportação a partir do texto do cabeçalho

Se você não deseja adicionar :EXPORT_FILE_NAME:propriedades a todos os títulos de nível superior, pode modificar org-export-allpara gerar o nome do arquivo automaticamente a partir de, por exemplo, o texto do título, definindo temporariamente :EXPORT_FILE_NAME:durante a exportação:

(defun org-export-all (backend)
  "Export all subtrees that are *not* tagged with :noexport: to
separate files.

Subtrees that do not have the :EXPORT_FILE_NAME: property set
are exported to a filename derived from the headline text."
  (interactive "sEnter backend: ")
  (let ((fn (cond ((equal backend "html") 'org-html-export-to-html)
                  ((equal backend "latex") 'org-latex-export-to-latex)
                  ((equal backend "pdf") 'org-latex-export-to-pdf)))
        (modifiedp (buffer-modified-p)))
    (save-excursion
      (set-mark (point-min))
      (goto-char (point-max))
      (org-map-entries
       (lambda ()
         (let ((export-file (org-entry-get (point) "EXPORT_FILE_NAME")))
           (unless export-file
             (org-set-property
              "EXPORT_FILE_NAME"
              (replace-regexp-in-string " " "_" (nth 4 (org-heading-components)))))
           (funcall fn nil t)
           (unless export-file (org-delete-property "EXPORT_FILE_NAME"))
           (set-buffer-modified-p modifiedp)))
       "-noexport" 'region-start-level))))

Essa função gera o nome do arquivo de exportação substituindo espaços por "_" no texto do título. Se você deseja gerar o nome do arquivo de outra maneira, altere o replace-regexp-in-stringsexp para o que quiser.

Gerando :EXPORT_FILE_NAME:ao definir:CUSTOM_ID:

Com o seguinte conselho, você org-set-propertydefinirá automaticamente um valor apropriado para :EXPORT_FILE_NAME:quando você definir :CUSTOM_ID::

(defadvice org-set-property (after set-export-file-name
                                   (property value) activate compile)
  (when (equal org-last-set-property "CUSTOM_ID")
    (let ((export-file-name
           (concat (org-entry-get nil "CUSTOM_ID")
                   "-"
                   (replace-regexp-in-string " " "-" (downcase (org-get-heading t t))))))
      (org-entry-put nil "EXPORT_FILE_NAME" export-file-name))))

Observe que isso não adicionará uma extensão de arquivo ao valor de, :EXPORT_FILE_NAME:mas isso não importa, porque ao exportar para um back-end específico, org-mode escolherá automaticamente a extensão correta para os arquivos resultantes .


Informação adicional

Atualizando subárvores existentes em massa

Se você tiver um grande número de subárvores existentes para as quais precisa definir a :EXPORT_FILE_NAME:propriedade, poderá usar uma macro de teclado . Posicione o ponto na primeira subárvore e faça o seguinte:

  • F3

    ... para começar a gravar.

  • C-c C-x p CUSTOM_ID RET RET

    ... para definir o Emacs com :EXPORT_FILE_NAME:base em :CUSTOM_ID:.

  • C-c C-f

    ... para passar para o próximo título de nível superior.

  • F4

    ... para parar a gravação.

Para repetir a macro para a próxima subárvore, pressione F4. Para repetir a macro para todas as subárvores restantes, pressione M-0 F4(que é zero).

Salvando macros para sessões futuras

Por padrão, as macros do teclado não são salvas nas sessões. Para armazenar a macro no seu arquivo init para uso posterior, faça o seguinte:

  1. Nome da macro:

    M-x name-last-kbd-macro RET org-set-export-file-name RET

  2. Encontre seu arquivo init e vá para um local onde você gostaria de inserir a macro.

  3. Insira a macro:

    M-x insert-kbd-macro RET org-set-export-file-name RET

    O Emacs irá inserir o seguinte código no ponto:

    (fset 'org-set-export-file-name
       "\C-c\C-xpCUSTOM_ID\C-m\C-m\C-c\C-f")

    Se você apertar os olhos com força, poderá ver que o segundo argumento para fsetcontém a sequência de teclas que você pressionou quando gravou a macro :)

  4. (Opcional) Para obter melhores resultados, convém vincular org-set-export-file-namea uma chave:

    (define-key org-mode-map (kbd "<f6>") 'org-set-export-file-name)
  5. Salve .

itsjeyd
fonte
1
Agradável. Você poderia me dar uma dica de como definir programaticamente a :EXPORT_FILE_NAME:propriedade :CUSTOM_ID:+heading-title-lowercasedpara cada cabeçalho?
GSL
1
@gsl Você pode aconselhar org-set-propertya gerar automaticamente a :EXPORT_FILE_NAME:propriedade quando definir :CUSTOM_ID:.
itsjeyd
1
Obrigado pelo conselho! Eu não sou tão fluente com isso elisp, mas vou tentar. Preciso descobrir como capturar o título de cada cabeçalho, substituir o espaço em branco pelo traço, colocar em letras minúsculas, adicionar essa sequência higienizada :CUSTOM_ID:e finalmente definir uma propriedade organizacional.
GSL
1
@gsl eu adicionei um defadvicepara a minha resposta que ajusta automaticamente :EXPORT_FILE_NAME:para <custom-id>-<heading>quando você definir :CUSTOM_ID:.
itsjeyd
1
Muito obrigado, eu aprendi muito com seu código. Se alguém já tiver um org-modearquivo com CUSTOM_IDs definido, como executar o código para definir "EXPORT_FILE_NAME"? Não haveria novas inserções. Eu estou supondo defadviceque não iria funcionar? Existe um recurso de loop para percorrer todos os títulos de nível superior e aplicar o código a eles?
GSL