Сохранение контекста при архивировании в org-режиме Emacs

Обычно я использую org-mode для отслеживания TODO. У меня есть файлы вроде:

* Misc.
** TODO Task 1
** TODO Task 2
* Project 1
** TODO Task 1
** TODO Task 2

В то время как архивирование целых поддеревьев, таких как Project 1, работает по назначению, я не могу переместить Task 1 в Misc. так, чтобы файл архива выглядел следующим образом (игнорируя PROPERTIES в этом примере):

* Misc
** DONE Task 1

Другими словами, я хочу, чтобы все разделы были подчинены задаче. Есть ли быстрый способ сделать это?

Ответ 1

Вероятно, вы хотите установить свойство :ARCHIVE: в родительских заголовках.

Он позволяет вам использовать специальные настройки org-archive-location для заголовка. (Смотрите manual)

Например, если ваш файл архива установлен как %s_archive, вы хотели бы, чтобы ваш исходный файл выглядел следующим образом:

* Misc.
  :PROPERTIES:
  :ARCHIVE:  %s_archive::* Misc
  :END:
** TODO Task 1
** TODO Task 2
* Project 1
  :PROPERTIES:
  :ARCHIVE:  %s_archive::* Project 1
  :END:
** TODO Task 1
** TODO Task 2

Это отправило бы любые поддеревья в * Misc в заголовок * Misc в архивном файле и выполнит эквивалент для поддеревьев в Project 1 (если вы не заархивируете все дерево за один снимок). Архивирование родительского дерева после того, как поддеревья, похоже, добавят его в качестве дополнительной подзаголовки под пунктом назначения. Он не поддерживает несколько уровней, поэтому вам нужно заранее настроить заголовки архивных файлов, чтобы гарантировать, что он выдает то, что вы хотите, если вам нужна сложная настройка такого типа.

Вы также можете использовать это свойство для архивирования определенных деревьев для разделения файлов (для целей экспорта/публикации/совместного использования).

Ответ 2

Я не думаю, что org-mode имеет поддержку прямого зеркалирования текущего контекста внутри вашего архивного файла.

Существует соответствующая переменная org-archive-location, которая может использоваться для указания одного заголовка для размещения вашего архивированного элемента, но несколько уровней внутри дерева не поддерживаются. На на этой странице есть два совета для org-archive-subtree, которые могут быть достаточно хорошими. Я копирую первый здесь, если сайт уходит:

(defadvice org-archive-subtree (around my-org-archive-subtree activate)
  (let ((org-archive-location
         (if (save-excursion (org-back-to-heading)
                             (> (org-outline-level) 1))
             (concat (car (split-string org-archive-location "::"))
                     "::* "
                     (car (org-get-outline-path)))
           org-archive-location)))
    ad-do-it))

Второй, и более сложный, также сохраняет теги, найденные в заголовках верхнего уровня.

Последнее, что может пригодиться, - это настраиваемая переменная org-archive-save-context-info. Если этот список содержит символ 'olpath, заархивированная запись будет содержать свойство :ARCHIVE_OLPATH:, которое будет установлено в контурный путь архивной записи (например, Projects/Misc. Возможно, вы можете выполнить некоторую пост-обработку на org-archive-subtree и переместите заархивированную запись в исходный контур, используя это.

Ответ 3

;; org-archive-subtree-hierarchical.el
;; modified from https://lists.gnu.org/archive/html/emacs-orgmode/2014-08/msg00109.html

;; In orgmode
;; * A
;; ** AA
;; *** AAA
;; ** AB
;; *** ABA
;; Archiving AA will remove the subtree from the original file and create
;; it like that in archive target:

;; * AA
;; ** AAA

;; And this give you
;; * A
;; ** AA
;; *** AAA


(require 'org-archive)

(defun org-archive-subtree-hierarchical--line-content-as-string ()
  "Returns the content of the current line as a string"
  (save-excursion
    (beginning-of-line)
    (buffer-substring-no-properties
     (line-beginning-position) (line-end-position))))

(defun org-archive-subtree-hierarchical--org-child-list ()
  "This function returns all children of a heading as a list. "
  (interactive)
  (save-excursion
    ;; this only works with org-version > 8.0, since in previous
    ;; org-mode versions the function (org-outline-level) returns
    ;; gargabe when the point is not on a heading.
    (if (= (org-outline-level) 0)
        (outline-next-visible-heading 1)
      (org-goto-first-child))
    (let ((child-list (list (org-archive-subtree-hierarchical--line-content-as-string))))
      (while (org-goto-sibling)
        (setq child-list (cons (org-archive-subtree-hierarchical--line-content-as-string) child-list)))
      child-list)))

(defun org-archive-subtree-hierarchical--org-struct-subtree ()
  "This function returns the tree structure in which a subtree
belongs as a list."
  (interactive)
  (let ((archive-tree nil))
    (save-excursion
      (while (org-up-heading-safe)
        (let ((heading
               (buffer-substring-no-properties
                (line-beginning-position) (line-end-position))))
          (if (eq archive-tree nil)
              (setq archive-tree (list heading))
            (setq archive-tree (cons heading archive-tree))))))
    archive-tree))

(defun org-archive-subtree-hierarchical ()
  "This function archives a subtree hierarchical"
  (interactive)
  (let ((org-tree (org-archive-subtree-hierarchical--org-struct-subtree))
        (this-buffer (current-buffer))
        (file (abbreviate-file-name
               (or (buffer-file-name (buffer-base-buffer))
                   (error "No file associated to buffer")))))
    (save-excursion
      (setq location (org-get-local-archive-location)
            afile (org-extract-archive-file location)
            heading (org-extract-archive-heading location)
            infile-p (equal file (abbreviate-file-name (or afile ""))))
      (unless afile
        (error "Invalid `org-archive-location'"))
      (if (> (length afile) 0)
          (setq newfile-p (not (file-exists-p afile))
                visiting (find-buffer-visiting afile)
                buffer (or visiting (find-file-noselect afile)))
        (setq buffer (current-buffer)))
      (unless buffer
        (error "Cannot access file \"%s\"" afile))
      (org-cut-subtree)
      (set-buffer buffer)
      (org-mode)
      (goto-char (point-min))
      (while (not (equal org-tree nil))
        (let ((child-list (org-archive-subtree-hierarchical--org-child-list)))
          (if (member (car org-tree) child-list)
              (progn
                (search-forward (car org-tree) nil t)
                (setq org-tree (cdr org-tree)))
            (progn
              (goto-char (point-max))
              (newline)
              (org-insert-struct org-tree)
              (setq org-tree nil)))))
      (newline)
      (org-yank)
      (when (not (eq this-buffer buffer))
        (save-buffer))
      (message "Subtree archived %s"
               (concat "in file: " (abbreviate-file-name afile))))))

(defun org-insert-struct (struct)
  "TODO"
  (interactive)
  (when struct
    (insert (car struct))
    (newline)
    (org-insert-struct (cdr struct))))

(defun org-archive-subtree ()
  (org-archive-subtree-hierarchical)
  )

Этот хак просто действует как рефикс в ваш файл архива со всей той же родительской структурой, без архива :PROPERTIES: здесь.

Также как сущность здесь: https://gist.github.com/CodeFalling/87b116291aa87fde72cb