Файл Tangle Org, содержащий директивы # + INCLUDE и несколько целей tangle

Oct 18 2020

Контекст

Я использую org-babel-tangleдля генерации своей конфигурации из файла организации config.org. Недавно я сделал небольшую уборку

  1. отправка config.orgв несколько файлов Org config/*.org;
  2. замените содержимое config.orgнесколькими #+INCLUDE: config/*.orgдирективами.

Для небольшой иллюстрации дерево каталогов выглядит следующим образом:

config/
|______ A.org
|______ B.org
|______ C.org
|
config.org

где

## config.org
#+INCLUDE: config/A.org
#+INCLUDE: config/B.org
#+INCLUDE: config/C.org
...

а также

## [ABC].org
#+BEGIN_SRC emacs-lisp :tangle target1.el
;; elisp code
#+END_SRC

#+BEGIN_SRC emacs-lisp :tangle target2.el
;; elisp code
#+END_SRC
...

Проблема и попытка

Однако я столкнулся с проблемой, org-babel-tangleкоторая не обрабатывает #+INCLUDEдирективы. Итак, я следую идее этого поста, который состоит из

  1. экспорт config.orgв файл Org config-export.orgс помощью org-export-to-file;
  2. org-babel-tangleэкспортированный файл Org, config-export.orgчтобы запутать цели.

Этот подход действительно заменяет несколько #+INCLUDEдиректив config.orgсодержимым config/*.org, но все параметры путаницы стираются . А именно, экспортированный config-export.orgфайл выглядит так (все целевые имена файлов emacs-lisp tangle теряются):

## config-export.org
#+BEGIN_SRC emacs-lisp
;; elisp code for target1.el
#+END_SRC

#+BEGIN_SRC emacs-lisp
;; elisp code for target2.el
#+END_SRC
...

Вопрос

Как org-babel-tangleполучить орг файл с #+INCLUDEдирективами по назначению?

( т.е. (1) включить все файлы Org (с сохранением :tangle target.el), затем (2) org-babel-tangleвесь файл Org.)

Связанный вопрос

Аналогичный вопрос был поднят в списке рассылки режима Org.

Ответы

2 NickD Oct 19 2020 at 23:26

Я не знаю, как это сделать, не меняя код (это могло быть возможно, org-babel-pre-tangle-hookно это не казалось совсем простым, поэтому я не пробовал). Но как только вы принимаете изменения кода, это уже не так уж плохо.

Самый простой способ, который я мог придумать, - это имитировать org-babel-tangleто, что org-export-asделают файлы, включенные в WRT. Простой тест (аналогичный тому, что вы описали выше) прошел легко (но см. Предостережения в конце), поэтому, если вы хотите попробовать воспроизвести изменение, я добавлю патч ниже.

Сам патч выглядел бы большим, но это только потому, что я беру весь код org-babel-tangleи заключаю его в другой слой, который меняет отступы, и это большая часть изменений. Игнорируя изменения пробелов, "реальное" изменение намного меньше: я получил патч ниже git diff -b, чтобы игнорировать пробелы. Затем вы можете видеть, что он очень маленький: мы просто заключаем почти все тело org-babel-tanglein (org-export-with-buffer-copy ...), чтобы мы могли выполнять преобразования текста во временном буфере и не нарушать оригинал, а затем добавляем еще одно преобразование для обработки #+INCLUDE:директив, прежде чем полученный буфер затем запутывается:

diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el
index b74b3fa0c..5e6eebf63 100644
--- a/lisp/ob-tangle.el
+++ b/lisp/ob-tangle.el
@@ -206,6 +206,9 @@ export file for all source blocks.  Optional argument LANG-RE can
 be used to limit the exported source code blocks by languages
 matching a regular expression."
   (interactive "P")
+  (org-export-with-buffer-copy
+   (org-export-expand-include-keyword)
+
    (run-hooks 'org-babel-pre-tangle-hook)
    ;; Possibly Restrict the buffer to the current code block
    (save-restriction
@@ -306,7 +309,7 @@ matching a regular expression."
     (mapc (lambda (pair)
         (when (cdr pair) (set-file-modes (car pair) (cdr pair))))
           path-collector)
-   (mapcar #'car path-collector)))))
+    (mapcar #'car path-collector))))))
 
 (defun org-babel-tangle-clean ()
   "Remove comments inserted by `org-babel-tangle'.

Это дает ожидаемые результаты. Я думаю, WRT запутанные файлы, но вы должны проверить, чтобы убедиться, что он работает правильно в вашем случае: мои тесты - это игрушечные тесты, поэтому они могут пропустить сложные ситуации, которые могут возникнуть в ваших тестах "реального мира" .

Есть одна проблема, которую я пока не понимаю: сам config.orgбуфер изменяется в моих тестах, несмотря на то org-export-with-copy-buffer, что в конце путаницы я должен отменить изменение C-x uи сохранить его. Вероятно, это потому, что я не понимаю, что org-export-with-copy-bufferна самом деле происходит, но я еще не проверял этот код.

OTOH, если это будет решено и все тесты пройдут успешно, мы можем предложить это как улучшение списка рассылки в режиме организации и решить эту проблему (с 2010 года!).

РЕДАКТИРОВАТЬ: Я не вижу никаких проблем с org-babel-tangle-file- он вызывает org-babel-tangleвнизу, поэтому теоретически он должен делать (почти) правильные вещи, и тестирование его на простом примере отлично подходит для меня. Может быть, вы можете опубликовать (небольшой) пример, который вызывает у вас проблемы, отредактировав свой вопрос?

Я также тестирую следующее обходное решение, чтобы обойти проблему с config.orgизменением:

#+begin_src emacs-lisp
(copy-file "./config.org.orig" "./config.org")
(org-babel-tangle-file "./config.org")
(delete-file "./config.org")
#+end_src

Уродливо, но вроде работает (за исключением того, что я должен ответить на вопрос «изменено на диске»).

2 Melioratus Oct 26 2020 at 09:30

Способ 1

Ручной метод с использованием привязок клавиш в организационном режиме по умолчанию

Настраивать

  1. Добавьте Orgв Org Babel Load Languagesконфигурацию, а затем примените и сохраните новую конфигурацию.

  2. Добавьте #+EXPORT_FILE_NAME:ключевое слово в верхней части config.org.

    например

    #+EXPORT_FILE_NAME: temp.org
    
  3. Добавьте #+PROPERTY: header-args:org :results drawer replaceключевое слово в верхней части config.org.

    например

    #+PROPERTY: header-args:org :results drawer replace
    
  4. Обновите, #+INCLUDE:чтобы включить src orgсинтаксис.

    например

    #+INCLUDE: config/A.org src org
    #+INCLUDE: config/B.org src org
    #+INCLUDE: config/C.org src org
    
  5. Добавьте :eval neverзаголовок к каждому из SRCблоков, которые будут запутываться внутри включенных config/*.orgфайлов.

    например

    #+BEGIN_SRC elisp :tangle tangle1.el :eval never 
      ;; Code goes here
    #+END_SRC
    

Шаги ручного выполнения

  1. Открыть config.orgв emacs.

    config.orgФайл должен быть похож на пример ниже:

    #+EXPORT_FILE_NAME: temp.org
    #+PROPERTY: header-args:org :results drawer replace 
    
    #+INCLUDE: config/A.org src org
    #+INCLUDE: config/B.org src org
    #+INCLUDE: config/C.org src org
    
  2. Экспорт config.orgс использованием C-c C-e O vключевого аккорда. Это создаст новый temp.orgфайл и откроется в emacs.

    temp.orgФайл должен быть похож на пример ниже:

    #+export_file_name: temp.org
    #+property: header-args:org :results drawer replace
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle1.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle2.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle3.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
  3. Выполнить все блоки в temp.orgиспользовании C-c C-v C-bключевого аккорда.

    temp.orgФайл должен быть похож на пример ниже:

    #+export_file_name: temp.org
    #+property: header-args:org :results drawer replace
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle1.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle1.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle2.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle2.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle3.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle3.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
  4. Выполните путаницу с temp.orgпомощью C-c C-v C-tключевого аккорда. Это запутает импортированные SRCблоки из config/*.orgфайлов.

    Это создаст запутанные файлы, подобные перечисленным ниже файлам:

    config.org
    tangle1.el
    tangle2.el
    tangle3.el
    temp.org
    
    ./config:
    A.org
    B.org
    C.org
    

Этот ответ был протестирован с использованием:
версия emacs: GNU Emacs 27.1
версия орг-режима: 9.3.7