diff --git a/README.org b/README.org index 251f0a27..173362af 100644 --- a/README.org +++ b/README.org @@ -264,7 +264,9 @@ This evil-collection package includes a lot of other evil based things. "ss" '(consult-line :which-key "consult search") "ww" '(other-window :which-key "other window") "wd" '(delete-window :which-key "other window") - )) + ) + (general-def 'minibuffer-local-map + "C-v" 'evil-paste-after)) #+end_src #+begin_src emacs-lisp @@ -510,78 +512,110 @@ This is the use-package definition with a lot of customization. Need to setup au Part of this config includes some special capture templates for my work as a youth minister. I create lessons through both org-mode and org-roam capture templates. The first part comes from org-roam, then the next is org-mode. #+begin_src emacs-lisp (use-package org - :config - (setq org-startup-indented t - org-edit-src-content-indentation 0) + :config + (setq org-startup-indented t + org-edit-src-content-indentation 0 + org-agenda-sticky t) - (add-hook 'org-mode-hook 'chris/org-mode-setup) + (add-hook 'org-mode-hook 'chris/org-mode-setup) - (org-babel-do-load-languages - 'org-babel-load-languages - '((emacs-lisp . t) - (python . t) - (shell . t))) + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (python . t) + (shell . t))) - (require 'org-tempo) - (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) - (add-to-list 'org-structure-template-alist '("py" . "src python")) - (add-to-list 'org-structure-template-alist '("sh" . "src shell")) - (add-to-list 'org-structure-template-alist '("q" . "quote")) + (require 'org-tempo) + (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) + (add-to-list 'org-structure-template-alist '("py" . "src python")) + (add-to-list 'org-structure-template-alist '("sh" . "src shell")) + (add-to-list 'org-structure-template-alist '("q" . "quote")) - (setq org-capture-templates - '(("t" "Personal todo" entry - (file+headline +org-capture-todo-file "Inbox") - "* TODO %^{TODO name}\nSCHEDULED: %T\n%a\n%i%?" :prepend t) - ("n" "Personal notes" entry - (file+headline +org-capture-notes-file "Inbox") - "* %u %?\n%i\n%a" :prepend t) - ("j" "Journal" entry - (file+olp+datetree +org-capture-journal-file) - "* %U %?\n%i\n%a" :prepend t) - ("p" "TFC Plan" entry - (function chris/org-roam-capture-lesson-file) - (file ".templates/tfcplantemplate.org") - :prepend nil - :jump-to-captured t - :empty-lines 1) - ("r" "Templates for projects") - ("rt" "Project-local todo" entry - (file+headline +org-capture-project-todo-file "Inbox") - "* TODO %?\n%i\n%a" :prepend t) - ("rn" "Project-local notes" entry - (file+headline +org-capture-project-notes-file "Inbox") - "* %U %?\n%i\n%a" :prepend t) - ("rc" "Project-local changelog" entry - (file+headline +org-capture-project-changelog-file "Unreleased") - "* %U %?\n%i\n%a" :prepend t) - ("o" "Centralized templates for projects") - ("ot" "Project todo" entry #'+org-capture-central-project-todo-file - "* TODO %?\n %i\n %a" :heading "Tasks" :prepend nil) - ("on" "Project notes" entry #'+org-capture-central-project-notes-file - "* %U %?\n %i\n %a" :heading "Notes" :prepend t) - ("oc" "Project changelog" entry #'+org-capture-central-project-changelog-file - "* %U %?\n %i\n %a" :heading "Changelog" :prepend t)) - org-capture-use-agenda-date t) + (setq org-capture-templates + '(("t" "Personal todo" entry + (file+headline +org-capture-todo-file "Inbox") + "* TODO %^{TODO name}\nSCHEDULED: %T\n%a\n%i%?" :prepend t) + ("n" "Personal notes" entry + (file+headline +org-capture-notes-file "Inbox") + "* %u %?\n%i\n%a" :prepend t) + ("j" "Journal" entry + (file+olp+datetree +org-capture-journal-file) + "* %U %?\n%i\n%a" :prepend t) + ("p" "TFC Plan" entry + (function chris/org-roam-capture-lesson-file) + (file ".templates/tfcplantemplate.org") + :prepend nil + :jump-to-captured t + :empty-lines 1) + ("r" "Templates for projects") + ("rt" "Project-local todo" entry + (file+headline +org-capture-project-todo-file "Inbox") + "* TODO %?\n%i\n%a" :prepend t) + ("rn" "Project-local notes" entry + (file+headline +org-capture-project-notes-file "Inbox") + "* %U %?\n%i\n%a" :prepend t) + ("rc" "Project-local changelog" entry + (file+headline +org-capture-project-changelog-file "Unreleased") + "* %U %?\n%i\n%a" :prepend t) + ("o" "Centralized templates for projects") + ("ot" "Project todo" entry #'+org-capture-central-project-todo-file + "* TODO %?\n %i\n %a" :heading "Tasks" :prepend nil) + ("on" "Project notes" entry #'+org-capture-central-project-notes-file + "* %U %?\n %i\n %a" :heading "Notes" :prepend t) + ("oc" "Project changelog" entry #'+org-capture-central-project-changelog-file + "* %U %?\n %i\n %a" :heading "Changelog" :prepend t)) + org-capture-use-agenda-date t) - ;;(setq org-superstar-headline-bullets-list '("◉" "◈" "▸" "✬" "◎" "◇" "❉" "✙" "❖")) - (setq org-imenu-depth 4) - (setq org-odt-styles-file "/home/chris/org/style.odt") + ;;(setq org-superstar-headline-bullets-list '("◉" "◈" "▸" "✬" "◎" "◇" "❉" "✙" "❖")) + (setq org-imenu-depth 4) + (setq org-odt-styles-file "/home/chris/org/style.odt") - (setq org-export-with-toc nil) - (setq org-export-with-author nil) - - (setq org-todo-keywords - '((sequence "TODO(t)" "PROJ(p)" "STRT(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "CNCL(c)") - (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))) + (setq org-export-with-toc nil) + (setq org-export-with-author nil) + + (setq org-todo-keywords + '((sequence "TODO(t)" "PROJ(p)" "STRT(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "CNCL(c)") + (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))) - (setq org-agenda-files - '("/home/chris/org/DMPREADME.org" "/home/chris/org/DMPTODO.org" "/home/chris/org/inbox.org" "/home/chris/org/notes.org" "/home/chris/org/repetition.org" "/home/chris/org/tasks.org" "/home/chris/org/tfc_plans.org" "/home/chris/org/ministry_team.org" "/home/chris/org/todo.org" "/home/chris/org/newsletter.org")) - (setq org-id-method 'ts) - :general - (chris/leader-keys "o a" 'org-agenda - "c" 'org-capture - "so" 'consult-imenu)) + (setq org-agenda-files + '("/home/chris/org/DMPREADME.org" "/home/chris/org/DMPTODO.org" "/home/chris/org/inbox.org" "/home/chris/org/notes.org" "/home/chris/org/repetition.org" "/home/chris/org/tasks.org" "/home/chris/org/tfc_plans.org" "/home/chris/org/ministry_team.org" "/home/chris/org/todo.org" "/home/chris/org/newsletter.org")) + (setq org-id-method 'ts) + + (defun chris/org-columns-view () + "Turn on org-columns overlay and turn off olivetti-mode" + (interactive) + (org-columns) + (olivetti-mode -1)) + + (defun chris/org-columns-quit () + "Remove the org-columns overlay and turn on olivetti-mode" + (interactive) + (org-columns-quit) + (olivetti-mode +1)) + + (defun chris/org-toggle-todo-and-mark-time () + "Mark the time of finishing task and mark it as done." + (interactive) + (save-excursion + (org-back-to-heading t) ;; Make sure command works even if point is + ;; below target heading + (cond ((looking-at "\*+ TODO") + (org-todo "DONE")) + ((looking-at "\*+ DONE") + (org-todo "TODO")) + (t (message "Can only toggle between TODO and DONE."))))) + :general + (chris/leader-keys "o a" 'org-agenda + "c" 'org-capture + "so" 'consult-imenu) + ('normal org-agenda-mode-map + "q" 'org-agenda-quit) + ('normal org-columns-map + "j" 'outline-next-heading + "h" 'outline-previous-heading) + ('normal org-mode-map + "RET" 'chris/org-dwim-at-point)) #+end_src We need to create a lesson capture function to find our lesson files differently each time we run our TFC plan capture. This is the most unique part of my capture template. This function uses =org-roam-find-file= to pick the lesson file that I need to add my lesson plan to. This way the lesson itself is created before the plan. @@ -709,6 +743,172 @@ In order to use it, I need to go to http://localhost:8080 (org-superstar-mode +1) (setq org-superstar-headline-bullets-list '("\u25c9" "\u25c8" "\u25b8" "\u25ce" "\u272c" "\u25c7" "\u2749" "\u2719" "\u2756"))) #+end_src +*** Org DWIM +I've stolen most of this code from Doom Emacs but we want to create a dwim-at-point function for Org-Mode that will fit under the =RET= key. This allows us to do a bunch of different functions depending on the context of point. +#+begin_src emacs-lisp +(defun chris/org-dwim-at-point (&optional arg) + "Do-what-I-mean at point. + +If on a: +- checkbox list item or todo heading: toggle it. +- clock: update its time. +- headline: cycle ARCHIVE subtrees, toggle latex fragments and inline images in + subtree; update statistics cookies/checkboxes and ToCs. +- footnote reference: jump to the footnote's definition +- footnote definition: jump to the first reference of this footnote +- table-row or a TBLFM: recalculate the table's formulas +- table-cell: clear it and go into insert mode. If this is a formula cell, + recaluclate it instead. +- babel-call: execute the source block +- statistics-cookie: update it. +- latex fragment: toggle it. +- link: follow it +- otherwise, refresh all inline images in current tree." + (interactive "P") + (let* ((context (org-element-context)) + (type (org-element-type context))) + ;; skip over unimportant contexts + (while (and context (memq type '(verbatim code bold italic underline strike-through subscript superscript))) + (setq context (org-element-property :parent context) + type (org-element-type context))) + (pcase type + (`headline + (cond ((memq (bound-and-true-p org-goto-map) + (current-active-maps)) + (org-goto-ret)) + ((and (fboundp 'toc-org-insert-toc) + (member "TOC" (org-get-tags))) + (toc-org-insert-toc) + (message "Updating table of contents")) + ((string= "ARCHIVE" (car-safe (org-get-tags))) + (org-force-cycle-archived)) + ((or (org-element-property :todo-type context) + (org-element-property :scheduled context)) + (org-todo + (if (eq (org-element-property :todo-type context) 'done) + (or (car (chris/org-get-todo-keywords-for (org-element-property :todo-keyword context))) + 'todo) + 'done)))) + ;; Update any metadata or inline previews in this subtree + (org-update-checkbox-count) + (org-update-parent-todo-statistics) + (when (and (fboundp 'toc-org-insert-toc) + (member "TOC" (org-get-tags))) + (toc-org-insert-toc) + (message "Updating table of contents")) + (let* ((beg (if (org-before-first-heading-p) + (line-beginning-position) + (save-excursion (org-back-to-heading) (point)))) + (end (if (org-before-first-heading-p) + (line-end-position) + (save-excursion (org-end-of-subtree) (point)))) + (overlays (ignore-errors (overlays-in beg end))) + (latex-overlays + (cl-find-if (lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay)) + overlays)) + (image-overlays + (cl-find-if (lambda (o) (overlay-get o 'org-image-overlay)) + overlays))) + (chris/org--toggle-inline-images-in-subtree beg end) + (if (or image-overlays latex-overlays) + (org-clear-latex-preview beg end) + (org--latex-preview-region beg end)))) + + (`clock (org-clock-update-time-maybe)) + + (`footnote-reference + (org-footnote-goto-definition (org-element-property :label context))) + + (`footnote-definition + (org-footnote-goto-previous-reference (org-element-property :label context))) + + ((or `planning `timestamp) + (org-follow-timestamp-link)) + + ((or `table `table-row) + (if (org-at-TBLFM-p) + (org-table-calc-current-TBLFM) + (ignore-errors + (save-excursion + (goto-char (org-element-property :contents-begin context)) + (org-call-with-arg 'org-table-recalculate (or arg t)))))) + + (`table-cell + (org-table-blank-field) + (org-table-recalculate arg) + (when (and (string-empty-p (string-trim (org-table-get-field))) + (bound-and-true-p evil-local-mode)) + (evil-change-state 'insert))) + + (`babel-call + (org-babel-lob-execute-maybe)) + + (`statistics-cookie + (save-excursion (org-update-statistics-cookies arg))) + + ((or `src-block `inline-src-block) + (org-babel-execute-src-block)) + + ((or `latex-fragment `latex-environment) + (org-latex-preview)) + + (`link + (let* ((lineage (org-element-lineage context '(link) t)) + (path (org-element-property :path lineage))) + (if (or (equal (org-element-property :type lineage) "img") + (and path (image-type-from-file-name path))) + (chris/org--toggle-inline-images-in-subtree + (org-element-property :begin lineage) + (org-element-property :end lineage)) + (org-open-at-point arg)))) + + (`paragraph + (let ((match (and (org-at-item-checkbox-p) (match-string 1)))) + (org-toggle-checkbox))) + + (_ + (if (or (org-in-regexp org-ts-regexp-both nil t) + (org-in-regexp org-tsr-regexp-both nil t) + (org-in-regexp org-link-any-re nil t)) + (call-interactively #'org-open-at-point) + (chris/org--toggle-inline-images-in-subtree + (org-element-property :begin context) + (org-element-property :end context))))))) + + +(defun chris/org-get-todo-keywords-for (&optional keyword) + "Returns the list of todo keywords that KEYWORD belongs to." + (when keyword + (cl-loop for (type . keyword-spec) + in (cl-remove-if-not #'listp org-todo-keywords) + for keywords = + (mapcar (lambda (x) (if (string-match "^\\([^(]+\\)(" x) + (match-string 1 x) + x)) + keyword-spec) + if (eq type 'sequence) + if (member keyword keywords) + return keywords))) + +(defun chris/org--toggle-inline-images-in-subtree (&optional beg end refresh) + "Refresh inline image previews in the current heading/tree." + (let ((beg (or beg + (if (org-before-first-heading-p) + (line-beginning-position) + (save-excursion (org-back-to-heading) (point))))) + (end (or end + (if (org-before-first-heading-p) + (line-end-position) + (save-excursion (org-end-of-subtree) (point))))) + (overlays (cl-remove-if-not (lambda (ov) (overlay-get ov 'org-image-overlay)) + (ignore-errors (overlays-in beg end))))) + (dolist (ov overlays nil) + (delete-overlay ov) + (setq org-inline-image-overlays (delete ov org-inline-image-overlays))) + (when (or refresh (not overlays)) + (org-display-inline-images t t beg end) + t))) +#+end_src ** Calendar #+begin_src emacs-lisp diff --git a/init.el b/init.el index 86746d7f..ce648e38 100644 --- a/init.el +++ b/init.el @@ -163,7 +163,9 @@ "ss" '(consult-line :which-key "consult search") "ww" '(other-window :which-key "other window") "wd" '(delete-window :which-key "other window") - )) + ) + (general-def 'minibuffer-local-map + "C-v" 'evil-paste-after)) (use-package evil-escape :after evil @@ -280,78 +282,110 @@ (display-line-numbers-mode -1)) (use-package org - :config - (setq org-startup-indented t - org-edit-src-content-indentation 0) + :config + (setq org-startup-indented t + org-edit-src-content-indentation 0 + org-agenda-sticky t) - (add-hook 'org-mode-hook 'chris/org-mode-setup) + (add-hook 'org-mode-hook 'chris/org-mode-setup) - (org-babel-do-load-languages - 'org-babel-load-languages - '((emacs-lisp . t) - (python . t) - (shell . t))) + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (python . t) + (shell . t))) - (require 'org-tempo) - (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) - (add-to-list 'org-structure-template-alist '("py" . "src python")) - (add-to-list 'org-structure-template-alist '("sh" . "src shell")) - (add-to-list 'org-structure-template-alist '("q" . "quote")) + (require 'org-tempo) + (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) + (add-to-list 'org-structure-template-alist '("py" . "src python")) + (add-to-list 'org-structure-template-alist '("sh" . "src shell")) + (add-to-list 'org-structure-template-alist '("q" . "quote")) - (setq org-capture-templates - '(("t" "Personal todo" entry - (file+headline +org-capture-todo-file "Inbox") - "* TODO %^{TODO name}\nSCHEDULED: %T\n%a\n%i%?" :prepend t) - ("n" "Personal notes" entry - (file+headline +org-capture-notes-file "Inbox") - "* %u %?\n%i\n%a" :prepend t) - ("j" "Journal" entry - (file+olp+datetree +org-capture-journal-file) - "* %U %?\n%i\n%a" :prepend t) - ("p" "TFC Plan" entry - (function chris/org-roam-capture-lesson-file) - (file ".templates/tfcplantemplate.org") - :prepend nil - :jump-to-captured t - :empty-lines 1) - ("r" "Templates for projects") - ("rt" "Project-local todo" entry - (file+headline +org-capture-project-todo-file "Inbox") - "* TODO %?\n%i\n%a" :prepend t) - ("rn" "Project-local notes" entry - (file+headline +org-capture-project-notes-file "Inbox") - "* %U %?\n%i\n%a" :prepend t) - ("rc" "Project-local changelog" entry - (file+headline +org-capture-project-changelog-file "Unreleased") - "* %U %?\n%i\n%a" :prepend t) - ("o" "Centralized templates for projects") - ("ot" "Project todo" entry #'+org-capture-central-project-todo-file - "* TODO %?\n %i\n %a" :heading "Tasks" :prepend nil) - ("on" "Project notes" entry #'+org-capture-central-project-notes-file - "* %U %?\n %i\n %a" :heading "Notes" :prepend t) - ("oc" "Project changelog" entry #'+org-capture-central-project-changelog-file - "* %U %?\n %i\n %a" :heading "Changelog" :prepend t)) - org-capture-use-agenda-date t) + (setq org-capture-templates + '(("t" "Personal todo" entry + (file+headline +org-capture-todo-file "Inbox") + "* TODO %^{TODO name}\nSCHEDULED: %T\n%a\n%i%?" :prepend t) + ("n" "Personal notes" entry + (file+headline +org-capture-notes-file "Inbox") + "* %u %?\n%i\n%a" :prepend t) + ("j" "Journal" entry + (file+olp+datetree +org-capture-journal-file) + "* %U %?\n%i\n%a" :prepend t) + ("p" "TFC Plan" entry + (function chris/org-roam-capture-lesson-file) + (file ".templates/tfcplantemplate.org") + :prepend nil + :jump-to-captured t + :empty-lines 1) + ("r" "Templates for projects") + ("rt" "Project-local todo" entry + (file+headline +org-capture-project-todo-file "Inbox") + "* TODO %?\n%i\n%a" :prepend t) + ("rn" "Project-local notes" entry + (file+headline +org-capture-project-notes-file "Inbox") + "* %U %?\n%i\n%a" :prepend t) + ("rc" "Project-local changelog" entry + (file+headline +org-capture-project-changelog-file "Unreleased") + "* %U %?\n%i\n%a" :prepend t) + ("o" "Centralized templates for projects") + ("ot" "Project todo" entry #'+org-capture-central-project-todo-file + "* TODO %?\n %i\n %a" :heading "Tasks" :prepend nil) + ("on" "Project notes" entry #'+org-capture-central-project-notes-file + "* %U %?\n %i\n %a" :heading "Notes" :prepend t) + ("oc" "Project changelog" entry #'+org-capture-central-project-changelog-file + "* %U %?\n %i\n %a" :heading "Changelog" :prepend t)) + org-capture-use-agenda-date t) - ;;(setq org-superstar-headline-bullets-list '("◉" "◈" "▸" "✬" "◎" "◇" "❉" "✙" "❖")) - (setq org-imenu-depth 4) - (setq org-odt-styles-file "/home/chris/org/style.odt") + ;;(setq org-superstar-headline-bullets-list '("◉" "◈" "▸" "✬" "◎" "◇" "❉" "✙" "❖")) + (setq org-imenu-depth 4) + (setq org-odt-styles-file "/home/chris/org/style.odt") - (setq org-export-with-toc nil) - (setq org-export-with-author nil) - - (setq org-todo-keywords - '((sequence "TODO(t)" "PROJ(p)" "STRT(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "CNCL(c)") - (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))) + (setq org-export-with-toc nil) + (setq org-export-with-author nil) + + (setq org-todo-keywords + '((sequence "TODO(t)" "PROJ(p)" "STRT(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "CNCL(c)") + (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))) - (setq org-agenda-files - '("/home/chris/org/DMPREADME.org" "/home/chris/org/DMPTODO.org" "/home/chris/org/inbox.org" "/home/chris/org/notes.org" "/home/chris/org/repetition.org" "/home/chris/org/tasks.org" "/home/chris/org/tfc_plans.org" "/home/chris/org/ministry_team.org" "/home/chris/org/todo.org" "/home/chris/org/newsletter.org")) - (setq org-id-method 'ts) - :general - (chris/leader-keys "o a" 'org-agenda - "c" 'org-capture - "so" 'consult-imenu)) + (setq org-agenda-files + '("/home/chris/org/DMPREADME.org" "/home/chris/org/DMPTODO.org" "/home/chris/org/inbox.org" "/home/chris/org/notes.org" "/home/chris/org/repetition.org" "/home/chris/org/tasks.org" "/home/chris/org/tfc_plans.org" "/home/chris/org/ministry_team.org" "/home/chris/org/todo.org" "/home/chris/org/newsletter.org")) + (setq org-id-method 'ts) + + (defun chris/org-columns-view () + "Turn on org-columns overlay and turn off olivetti-mode" + (interactive) + (org-columns) + (olivetti-mode -1)) + + (defun chris/org-columns-quit () + "Remove the org-columns overlay and turn on olivetti-mode" + (interactive) + (org-columns-quit) + (olivetti-mode +1)) + + (defun chris/org-toggle-todo-and-mark-time () + "Mark the time of finishing task and mark it as done." + (interactive) + (save-excursion + (org-back-to-heading t) ;; Make sure command works even if point is + ;; below target heading + (cond ((looking-at "\*+ TODO") + (org-todo "DONE")) + ((looking-at "\*+ DONE") + (org-todo "TODO")) + (t (message "Can only toggle between TODO and DONE."))))) + :general + (chris/leader-keys "o a" 'org-agenda + "c" 'org-capture + "so" 'consult-imenu) + ('normal org-agenda-mode-map + "q" 'org-agenda-quit) + ('normal org-columns-map + "j" 'outline-next-heading + "h" 'outline-previous-heading) + ('normal org-mode-map + "RET" 'chris/org-dwim-at-point)) (defun chris/org-roam-capture-lesson-file () "Function to return the lesson file that is needed for TFC plan capture and move to correct position for plan insertion" @@ -452,6 +486,169 @@ (org-superstar-mode +1) (setq org-superstar-headline-bullets-list '("\u25c9" "\u25c8" "\u25b8" "\u25ce" "\u272c" "\u25c7" "\u2749" "\u2719" "\u2756"))) +(defun chris/org-dwim-at-point (&optional arg) + "Do-what-I-mean at point. + +If on a: +- checkbox list item or todo heading: toggle it. +- clock: update its time. +- headline: cycle ARCHIVE subtrees, toggle latex fragments and inline images in + subtree; update statistics cookies/checkboxes and ToCs. +- footnote reference: jump to the footnote's definition +- footnote definition: jump to the first reference of this footnote +- table-row or a TBLFM: recalculate the table's formulas +- table-cell: clear it and go into insert mode. If this is a formula cell, + recaluclate it instead. +- babel-call: execute the source block +- statistics-cookie: update it. +- latex fragment: toggle it. +- link: follow it +- otherwise, refresh all inline images in current tree." + (interactive "P") + (let* ((context (org-element-context)) + (type (org-element-type context))) + ;; skip over unimportant contexts + (while (and context (memq type '(verbatim code bold italic underline strike-through subscript superscript))) + (setq context (org-element-property :parent context) + type (org-element-type context))) + (pcase type + (`headline + (cond ((memq (bound-and-true-p org-goto-map) + (current-active-maps)) + (org-goto-ret)) + ((and (fboundp 'toc-org-insert-toc) + (member "TOC" (org-get-tags))) + (toc-org-insert-toc) + (message "Updating table of contents")) + ((string= "ARCHIVE" (car-safe (org-get-tags))) + (org-force-cycle-archived)) + ((or (org-element-property :todo-type context) + (org-element-property :scheduled context)) + (org-todo + (if (eq (org-element-property :todo-type context) 'done) + (or (car (chris/org-get-todo-keywords-for (org-element-property :todo-keyword context))) + 'todo) + 'done)))) + ;; Update any metadata or inline previews in this subtree + (org-update-checkbox-count) + (org-update-parent-todo-statistics) + (when (and (fboundp 'toc-org-insert-toc) + (member "TOC" (org-get-tags))) + (toc-org-insert-toc) + (message "Updating table of contents")) + (let* ((beg (if (org-before-first-heading-p) + (line-beginning-position) + (save-excursion (org-back-to-heading) (point)))) + (end (if (org-before-first-heading-p) + (line-end-position) + (save-excursion (org-end-of-subtree) (point)))) + (overlays (ignore-errors (overlays-in beg end))) + (latex-overlays + (cl-find-if (lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay)) + overlays)) + (image-overlays + (cl-find-if (lambda (o) (overlay-get o 'org-image-overlay)) + overlays))) + (chris/org--toggle-inline-images-in-subtree beg end) + (if (or image-overlays latex-overlays) + (org-clear-latex-preview beg end) + (org--latex-preview-region beg end)))) + + (`clock (org-clock-update-time-maybe)) + + (`footnote-reference + (org-footnote-goto-definition (org-element-property :label context))) + + (`footnote-definition + (org-footnote-goto-previous-reference (org-element-property :label context))) + + ((or `planning `timestamp) + (org-follow-timestamp-link)) + + ((or `table `table-row) + (if (org-at-TBLFM-p) + (org-table-calc-current-TBLFM) + (ignore-errors + (save-excursion + (goto-char (org-element-property :contents-begin context)) + (org-call-with-arg 'org-table-recalculate (or arg t)))))) + + (`table-cell + (org-table-blank-field) + (org-table-recalculate arg) + (when (and (string-empty-p (string-trim (org-table-get-field))) + (bound-and-true-p evil-local-mode)) + (evil-change-state 'insert))) + + (`babel-call + (org-babel-lob-execute-maybe)) + + (`statistics-cookie + (save-excursion (org-update-statistics-cookies arg))) + + ((or `src-block `inline-src-block) + (org-babel-execute-src-block)) + + ((or `latex-fragment `latex-environment) + (org-latex-preview)) + + (`link + (let* ((lineage (org-element-lineage context '(link) t)) + (path (org-element-property :path lineage))) + (if (or (equal (org-element-property :type lineage) "img") + (and path (image-type-from-file-name path))) + (chris/org--toggle-inline-images-in-subtree + (org-element-property :begin lineage) + (org-element-property :end lineage)) + (org-open-at-point arg)))) + + (`paragraph + (let ((match (and (org-at-item-checkbox-p) (match-string 1)))) + (org-toggle-checkbox))) + + (_ + (if (or (org-in-regexp org-ts-regexp-both nil t) + (org-in-regexp org-tsr-regexp-both nil t) + (org-in-regexp org-link-any-re nil t)) + (call-interactively #'org-open-at-point) + (chris/org--toggle-inline-images-in-subtree + (org-element-property :begin context) + (org-element-property :end context))))))) + + +(defun chris/org-get-todo-keywords-for (&optional keyword) + "Returns the list of todo keywords that KEYWORD belongs to." + (when keyword + (cl-loop for (type . keyword-spec) + in (cl-remove-if-not #'listp org-todo-keywords) + for keywords = + (mapcar (lambda (x) (if (string-match "^\\([^(]+\\)(" x) + (match-string 1 x) + x)) + keyword-spec) + if (eq type 'sequence) + if (member keyword keywords) + return keywords))) + +(defun chris/org--toggle-inline-images-in-subtree (&optional beg end refresh) + "Refresh inline image previews in the current heading/tree." + (let ((beg (or beg + (if (org-before-first-heading-p) + (line-beginning-position) + (save-excursion (org-back-to-heading) (point))))) + (end (or end + (if (org-before-first-heading-p) + (line-end-position) + (save-excursion (org-end-of-subtree) (point))))) + (overlays (cl-remove-if-not (lambda (ov) (overlay-get ov 'org-image-overlay)) + (ignore-errors (overlays-in beg end))))) + (dolist (ov overlays nil) + (delete-overlay ov) + (setq org-inline-image-overlays (delete ov org-inline-image-overlays))) + (when (or refresh (not overlays)) + (org-display-inline-images t t beg end) + t))) + (use-package calfw :commands chris/calfw-calendar-open :config