diff --git a/core/core-popups.el b/core/core-popups.el index e96b64ba8..b02bcaae6 100644 --- a/core/core-popups.el +++ b/core/core-popups.el @@ -348,43 +348,66 @@ the command buffer." (add-hook! org-load - ;; Ensures org-src-edit yields control of its buffer to shackle. - (defun doom*org-src-switch-to-buffer (buffer context) (pop-to-buffer buffer)) - (advice-add 'org-src-switch-to-buffer :override 'doom*org-src-switch-to-buffer) + (set! :popup + '("*Calendar*" :size 0.4 :noselect t) + '(" *Org todo*" :size 5 :noselect t) + '("*Org Note*" :size 10) + '("*Org Select*" :size 20 :noselect t) + '("*Org Links*" :size 5 :noselect t) + '(" *Agenda Commands*" :noselect t) + '("^\\*Org Agenda" :regexp t :size 0.4) + '("*Org Clock*" :noselect t) + '("*Edit Formulas*" :size 10) + '("\\*Org Src" :regexp t :size 15) + '("^\\*Org-Babel" :regexp t :size 0.4) + '("^CAPTURE.*\\.org$" :regexp t :size 20)) - ;; ...for org-todo, org-link and org-agenda popups - (defun doom*org-pop-to-buffer-same-window (&optional buffer-or-name norecord label) - "Pop to buffer specified by BUFFER-OR-NAME in the selected window." - (display-buffer buffer-or-name)) - (advice-add 'org-pop-to-buffer-same-window :override 'doom*org-pop-to-buffer-same-window) + ;; Org tries to do its own popup management, causing buffer/window config + ;; armageddon when paired with shackle. To fix this, first we suppress + ;; delete-other-windows in org functions: + (defun doom*suppress-delete-other-windows (orig-fn &rest args) + (cl-flet ((silence (&rest args) (ignore))) + (advice-add 'delete-other-windows :around #'silence) + (unwind-protect + (apply orig-fn args) + (advice-remove 'delete-other-windows #'silence)))) + (advice-add 'org-capture-place-template :around #'doom*suppress-delete-other-windows) + (advice-add 'org-agenda :around #'doom*suppress-delete-other-windows) + (advice-add 'org-add-log-note :around #'doom*suppress-delete-other-windows) + ;; Tell org-src-edit to open another window, which shackle can intercept. + (setq org-src-window-setup 'other-window) + + ;; Then, we tell org functions to use pop-to-buffer instead of + ;; switch-to-buffer-*. Buffers get handed off to shackle properly this way. (defun doom*org-switch-to-buffer-other-window (&rest args) - (car-safe - (mapc (lambda (b) - (let ((buf (if (stringp b) (get-buffer-create b) b))) - (pop-to-buffer buf t t))) - args))) + (pop-to-buffer (car args))) (advice-add 'org-switch-to-buffer-other-window :override 'doom*org-switch-to-buffer-other-window) - (defun doom/popup-org-agenda-quit () - "Necessary to finagle org-agenda into shackle popups & behave on quit." - (interactive) - (if org-agenda-columns-active - (org-columns-quit) - (let ((buf (current-buffer))) - (and (not (eq org-agenda-window-setup 'current-window)) - (not (one-window-p)) - (delete-window)) - (kill-buffer buf) - (setq org-agenda-archives-mode nil - org-agenda-buffer nil)))) + ;; ...for org-todo, org-link and org-agenda popups + ;; (defun doom*org-pop-to-buffer-same-window (&optional buffer-or-name norecord label) + ;; "Pop to buffer specified by BUFFER-OR-NAME in the selected window." + ;; (switch-to-buffer buffer-or-name)) + ;; (advice-add 'org-pop-to-buffer-same-window :override 'doom*org-pop-to-buffer-same-window) + + ;; (defun doom/popup-org-agenda-quit () + ;; "Necessary to finagle org-agenda into shackle popups & behave on quit." + ;; (interactive) + ;; (if org-agenda-columns-active + ;; (org-columns-quit) + ;; (let ((buf (current-buffer))) + ;; (and (not (eq org-agenda-window-setup 'current-window)) + ;; (not (one-window-p)) + ;; (delete-window)) + ;; (kill-buffer buf) + ;; (setq org-agenda-archives-mode nil + ;; org-agenda-buffer nil)))) (after! org-agenda (after! evil - (evil-define-key* 'motion org-agenda-mode-map - [escape] 'doom/popup-org-agenda-quit - (kbd "ESC") 'doom/popup-org-agenda-quit)) - + (map! :map* org-agenda-mode-map + :m [escape] 'doom/popup-org-agenda-quit + :m "ESC" 'doom/popup-org-agenda-quit)) (let ((map org-agenda-mode-map)) (define-key map "q" 'doom/popup-org-agenda-quit) (define-key map "Q" 'doom/popup-org-agenda-quit)))) diff --git a/modules/lang/org/+capture.el b/modules/lang/org/+capture.el index 3167a3e36..d1d3fbce4 100644 --- a/modules/lang/org/+capture.el +++ b/modules/lang/org/+capture.el @@ -1,31 +1,26 @@ ;;; lang/org/+capture.el --- -*- no-byte-compile: t; -*- -;; Sets up a sane `org-capture' workflow, wherein the org-capture buffer is -;; opened in a popup frame, and can be invoked from outside Emacs as well. +;; Sets up two `org-capture' workflows that I like: ;; -;; See `+org/capture' +;; 1. The traditional way: invoking `org-capture' directly (or through a +;; command, like :org). +;; +;; 2. Through a org-capture popup frame that is invoked from outside Emacs (the +;; script is below). This lets me open an org-capture box anywhere I can call +;; org-capture.sh, like, say, from qutebrowser or vimperator. +;; +;; #!/usr/bin/env bash +;; emacsclient -c \ +;; -F "((name . \"org-capture\") (height . 25) (width . 70))" \ +;; --eval "(org-capture nil \"${1:-n}\")" +;; +;; Place this in, say, org-capture.sh somewhere in your $PATH. (add-hook '+org-init-hook '+org|init-capture t) (defun +org|init-capture () "Set up a sane `org-capture' workflow." - (setq org-default-notes-file +org-quicknote-dir) - - (require 'org-capture) - (require 'org-protocol) - (set! :popup "*Org Select*" :size 0.4) - - (defadvice org-capture (after make-full-window-frame activate) - "If org-capture creates a new frame, this initializes it properly, by -deleting other windows and blanking out the mode-line." - (when (equal "org-capture" (frame-parameter nil 'name)) - (setq mode-line-format nil) - (delete-other-windows))) - - (defadvice org-capture-finalize (after delete-capture-frame activate) - "Closes the frame once org-capture is done." - (when (equal "org-capture" (frame-parameter nil 'name)) - (delete-frame))) + (setq org-default-notes-file (concat +org-dir "notes.org")) (setq org-capture-templates '(;; TODO: New Task (todo) @@ -44,11 +39,30 @@ deleting other windows and blanking out the mode-line." ;; "* %u %?\n%i" :prepend t) ("n" "Notes" entry - (file+headline org-default-notes-file "Inbox") + (file+headline (concat +org-dir "notes.org") "Inbox") "* %u %?\n%i" :prepend t) ;; ("v" "Vocab" entry ;; (file+headline (concat org-directory "topics/vocab.org") "Unsorted") ;; "** %i%?\n") - ))) + )) + + ;; Allows the Emacs mini-frame (opened from an external shell script to run + ;; and clean up properly) if the frame is named "org-capture". + (require 'org-capture) + (require 'org-protocol) + (defun +org*capture-init (&rest _) + "Makes sure the org-capture window is the only window in the frame." + (when (equal "org-capture" (frame-parameter nil 'name)) + (setq mode-line-format nil) + (delete-other-windows))) + (advice-add 'org-capture :after '+org*capture-init) + + (defun +org|capture-finalize () + "Closes the frame once org-capture is done." + (when (equal "org-capture" (frame-parameter nil 'name)) + (when (and (featurep 'persp-mode) persp-mode) + (+workspace/delete (+workspace-current-name))) + (delete-frame))) + (add-hook 'org-capture-after-finalize-hook '+org|capture-finalize)) diff --git a/modules/lang/org/autoload/capture.el b/modules/lang/org/autoload/capture.el index bc3aed03f..07ac217e2 100644 --- a/modules/lang/org/autoload/capture.el +++ b/modules/lang/org/autoload/capture.el @@ -1,18 +1,3 @@ ;;; lang/org/autoload/capture.el -;;;###autoload -(defun +org/capture (&optional template string) - "Run `org-capture' in a new, disposable popup frame." - (interactive) - (let ((org-capture-entry (org-capture-select-template template))) - (cond ((equal org-capture-entry "C") - (find-file (expand-file-name "module-org-notes.el" doom-modules-dir)) - (re-search-forward "^\\s-+(setq org-capture-templates" (point-max) t) - (recenter)) - ((not (equal org-capture-entry "q")) - (let ((frame (make-frame '((name . "org-capture") (height . 15) (width . 80))))) - (with-selected-frame frame - (if string - (org-capture-string string) - (org-capture)))))))) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index a4c3e18fe..37f0a1f3e 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -17,17 +17,13 @@ (add-hook 'org-mode-hook '+org|hook) ;; Custom variables -(defvar +org-dir "~/work/org/" +(defvar +org-dir (expand-file-name "~/work/org/") "The directory where org files are kept.") - (defvaralias 'org-directory '+org-dir) (defvar +org-notes-dir (concat +org-dir "notes") "The directory where the notes are kept") -(defvar +org-quicknote-dir (concat +org-dir "inbox") - "The directory to store quick notes produced by `doom:org-capture' (individual org files)") - (defvar +org-attachment-dir ".attach/" "Where to store attachments (relative to current org file).") @@ -59,7 +55,7 @@ (defun +org|update-cookies () "Update counts on headlines (\"cookies\")." - (when (file-exists-p buffer-file-name) + (when (and buffer-file-name (file-exists-p buffer-file-name)) (org-update-statistics-cookies t))) (add-hook 'before-save-hook '+org|update-cookies nil t) @@ -75,15 +71,6 @@ :keymap (make-sparse-keymap) :group 'evil-org) - (set! :popup - '(" *Agenda Commands*" :size 30 :noselect t) - '(" *Org todo*" :size 5 :noselect t) - '("*Calendar*" :size 0.4 :noselect t) - '("*Org Links*" :size 5 :noselect t) - '("^\\*Org Agenda.+" :size 0.4 :regexp t) - '("^\\*Org Src .+\\*$" :size 0.4 :regexp t) - '("^\\*Org-Babel.*\\*$" :size 0.4 :regexp t)) - (setq-default org-export-coding-system 'utf-8