Как контролировать, где отображается буфер ключевых слов org todo?

9

Вопрос : как я могу контролировать, где orgпоявляется буфер ключевых слов todo?

Ввод todoключевого слова с помощью C-c C-t( org-todo) открывает новый буфер с параметрами ключевого слова, а затем снова закрывает его после выбора. Все идет нормально. Тем не менее, для этого требуется другое окно, что не очень хорошо, тем более что ему действительно нужно отображать только одну или две строки с ключевыми словами.

Таким образом, при следующей компоновке нажатие C-c C-tв левом окне ( some-org-buffer) откроет *Org todo*в правом окне:

+---------------------+---------------------+
|                     |                     |
|                     |                     |
|                     |                     |
|                     |                     |
|   some-org-buffer   |  some-other-buffer  |
|                     |                     |
|                     |                     |
|                     |                     |
|                     |                     |
+---------------------+---------------------+

Вместо этого я хотел бы, чтобы небольшое окно всплыло как вертикальное разделение, как показано ниже:

+---------------------+---------------------+
|                     |                     |
|                     |                     |
|   some-org-buffer   |  some-other-buffer  |
|                     |                     |
|                     |                     |
+---------------------+                     |
|                     |                     |
|     *Org todo*      |                     |
|                     |                     |
+---------------------+---------------------+

Пользуясь этим ответом , я написал функцию для добавления в display-buffer-alist:

(defun org-todo-position (buffer alist)
  (let ((win (car (cl-delete-if-not
                   (lambda (window)
                     (with-current-buffer (window-buffer window)
                       (memq major-mode
                             '(org-mode org-agenda-mode))))
                   (window-list)))))
    (when win
      (let ((new (split-window win -5 'below)))
        (set-window-buffer new buffer)
        new))))

(add-to-list 'display-buffer-alist
             (list " \\*Org todo\\*" #'dan-org-todo-position))

Однако это не работает. Вздох. Что я сделал не так с display-buffer-alist? Если говорить более конкретно, как я могу получить todoбуфер ключевых слов, чтобы он появлялся там, где я хочу?

Дэн
источник
Не ответ на ваш вопрос, но вы можете рассмотреть возможность изменения, org-switch-to-buffer-other-windowчтобы сделать то, что вы хотите - вы можете создать условие, которое делает все, что вы хотите.
юрист
@lawlist: спасибо, я покопался и узнал о org-switch-to-buffer-other-windowцелой куче других уродливых orgвнутренностей. Смотрите ответ на позорное «решение».
Дан
Включая все это в мой .emacs, я внимательно посмотрел на вашу функцию позиционирования и чувствую, что что-то упускаю из-за вашего определения win. Есть ли причина, которую вы не можете просто использовать (selected-window)здесь?
Аарон Харрис
@AaronHarris: еще не пробовал; Я (слегка) адаптировал ответ из другого поста, в котором использовался этот механизм. Сделайте попытку и посмотрите, работает ли это.
Дан
@ Дан: Да, это работает. Я просто немного волновался, что из-за какой-то ситуации из-за этого ситуация может взорваться. И спасибо за эту функцию, кстати. Он делает именно то, что я хочу, и я не думаю, что смог бы сформулировать, что я хотел, прежде чем увидеть этот вопрос.
Аарон Харрис

Ответы:

4

Вздох. Я нашел «решение», но уродливое, которое требует перезаписи существующей orgфункции. Я бы предпочел тот, который не требует изменения orgисходного кода, но я помещаю результаты моего археологического исследования здесь на тот случай, если кто-то еще сможет его использовать.

Как я заметил в прошлом с другими orgфункциями, org-modeон довольно самоуверен в том, как он обрабатывает окна.

Глубоко в исходном коде org-todoнаходится вызов функции org-fast-todo-selection. Эта функция, в свою очередь, вызывает org-switch-to-buffer-other-window, чья строка документации читает, частично:

Переключиться на буфер во втором окне текущего кадра. В частности, не допускаются всплывающие окна .

Ооо

Хорошо, я укушу: давайте посмотрим org-switch-to-buffer-other-window. Он использует org-no-popups макрос. Его строка документа гласит:

Подавить всплывающие окна. Позвольте связать некоторые переменные с нулем вокруг тела ...

Оказывается, это display-buffer-alistодна из переменных, letпривязанная к nil.

( Голова взрывается. )

В конечном счете, мы можем заставить его не игнорировать display-buffer-alist, отредактировав org-fast-todo-selectionи заменив org-switch-to-buffer-other-windowстроку на старую switch-to-buffer-other-window:

(defun org-fast-todo-selection ()
  "Fast TODO keyword selection with single keys.
Returns the new TODO keyword, or nil if no state change should occur."
  (let* ((fulltable org-todo-key-alist)
     (done-keywords org-done-keywords) ;; needed for the faces.
     (maxlen (apply 'max (mapcar
                  (lambda (x)
                (if (stringp (car x)) (string-width (car x)) 0))
                  fulltable)))
     (expert nil)
     (fwidth (+ maxlen 3 1 3))
     (ncol (/ (- (window-width) 4) fwidth))
     tg cnt e c tbl
     groups ingroup)
    (save-excursion
      (save-window-excursion
    (if expert
        (set-buffer (get-buffer-create " *Org todo*"))
      ;; (org-switch-to-buffer-other-window (get-buffer-create " *Org todo*")))
      (switch-to-buffer-other-window (get-buffer-create " *Org todo*")))
    (erase-buffer)
    (org-set-local 'org-done-keywords done-keywords)
    (setq tbl fulltable cnt 0)
    (while (setq e (pop tbl))
      (cond
       ((equal e '(:startgroup))
        (push '() groups) (setq ingroup t)
        (when (not (= cnt 0))
          (setq cnt 0)
          (insert "\n"))
        (insert "{ "))
       ((equal e '(:endgroup))
        (setq ingroup nil cnt 0)
        (insert "}\n"))
       ((equal e '(:newline))
        (when (not (= cnt 0))
          (setq cnt 0)
          (insert "\n")
          (setq e (car tbl))
          (while (equal (car tbl) '(:newline))
        (insert "\n")
        (setq tbl (cdr tbl)))))
       (t
        (setq tg (car e) c (cdr e))
        (if ingroup (push tg (car groups)))
        (setq tg (org-add-props tg nil 'face
                    (org-get-todo-face tg)))
        (if (and (= cnt 0) (not ingroup)) (insert "  "))
        (insert "[" c "] " tg (make-string
                   (- fwidth 4 (length tg)) ?\ ))
        (when (= (setq cnt (1+ cnt)) ncol)
          (insert "\n")
          (if ingroup (insert "  "))
          (setq cnt 0)))))
    (insert "\n")
    (goto-char (point-min))
    (if (not expert) (org-fit-window-to-buffer))
    (message "[a-z..]:Set [SPC]:clear")
    (setq c (let ((inhibit-quit t)) (read-char-exclusive)))
    (cond
     ((or (= c ?\C-g)
          (and (= c ?q) (not (rassoc c fulltable))))
      (setq quit-flag t))
     ((= c ?\ ) nil)
     ((setq e (rassoc c fulltable) tg (car e))
      tg)
     (t (setq quit-flag t)))))))
Дэн
источник
Не могли бы вы использовать cl fletздесь, чтобы привязать org-switch-to-buffer-other-window? Хотите знать, если вы можете посоветовать или иным образом обернуть, org-fast-todo-selectionа не перезаписать его.
glucas
@glucas: хорошая идея. Попробовал и не смог заставить его работать, хотя.
Дан
1
@ Дэн, я смог заставить это работать, переопределив org-switch-to-buffer-other-windowпросто (switch-to-buffer-other-window args). Я также удалил &restиз функции аргументы.
sk8ingdom
3

Недавно я понял, как сделать захват в режиме Org в новом кадре . Изменить этот код для использования вашей функции довольно просто. Вот как я это сделаю:

(defun my/org-todo-window-advice (orig-fn)
  "Advice to fix window placement in `org-fast-todo-selection'."
  (let  ((override '("\\*Org todo\\*" dan-org-todo-position)))
    (add-to-list 'display-buffer-alist override)
    (my/with-advice
        ((#'org-switch-to-buffer-other-window :override #'pop-to-buffer))
      (unwind-protect (funcall orig-fn)
        (setq display-buffer-alist
              (delete override display-buffer-alist))))))

(advice-add #'org-fast-todo-selection :around #'my/org-todo-window-advice)

Для полноты, вот определение my/with-adviceмакроса из другого ответа:

(defmacro my/with-advice (adlist &rest body)
  "Execute BODY with temporary advice in ADLIST.

Each element of ADLIST should be a list of the form
  (SYMBOL WHERE FUNCTION [PROPS])
suitable for passing to `advice-add'.  The BODY is wrapped in an
`unwind-protect' form, so the advice will be removed even in the
event of an error or nonlocal exit."
  (declare (debug ((&rest (&rest form)) body))
           (indent 1))
  `(progn
     ,@(mapcar (lambda (adform)
                 (cons 'advice-add adform))
               adlist)
     (unwind-protect (progn ,@body)
       ,@(mapcar (lambda (adform)
                   `(advice-remove ,(car adform) ,(nth 2 adform)))
                 adlist))))
Аарон Харрис
источник
2

Просматривая чужой конфиг , я нашел способ контролировать, где *Org Src.*и где *Org todo*появляются буферы. Теперь, когда я нажимаю C-c C-tили C-c 'эти буферы отображаются в новом окне в нижней части моего текущего макета окна, и выбор состояния TODO или нажатие C-c '( org-edit-src-exit) возвращает мою конфигурацию окна в исходное расположение.

Это решение включает в себя использование кандалов и некоторых Elisp:

Шаг 1

Скачать shackleс MELPA. Вот как я настроил это use-packageв моем файле инициализации:

(use-package shackle
    :ensure t
    :diminish shackle-mode  ; hide name in mode-line
    :config
    (setq shackle-rules
          '(("\\*Org Src.*" :regexp t :align below :select t)
            (" *Org todo*" :align below :select t)))
    (shackle-mode t))

Важные настройки здесь:

(setq shackle-rules
              '(("\\*Org Src.*" :regexp t :align below :select t)
                (" *Org todo*" :align below :select t)))

Источник

Обратите особое внимание на пространство между "и *Orgв нижней строке. Я не тестировал настройки :alignв других местах, но, shackleпохоже, довольно гибок, поэтому стоит прочитать документацию и поэкспериментировать с ней.

Шаг 2

Теперь мы заставляем org-modeслушать еще shackleнемного Elisp в нашем файле инициализации. Чтобы получить *Org Src.*буферы для подражания shackle-rules:

(setq org-src-window-setup 'other-window)

Источник

К сожалению org-mode, не предоставляет аналогичные настройки для *Org todo*буферов. Этот хак восполняет это (и, вероятно, делает (setq org-src-window-setup 'other-window)излишним, но кто-то более знающий должен будет вмешаться):

;; Re-define org-switch-to-buffer-other-window to NOT use org-no-popups.
;; Primarily for compatibility with shackle.
(defun org-switch-to-buffer-other-window (args)
  "Switch to buffer in a second window on the current frame.
In particular, do not allow pop-up frames.
Returns the newly created buffer.
Redefined to allow pop-up windows."
  ;;  (org-no-popups
  ;;     (apply 'switch-to-buffer-other-window args)))
  (switch-to-buffer-other-window args))

Источник

ученичество
источник