diff options
| author | Craig Jennings <c@cjennings.net> | 2024-04-22 12:23:13 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2024-04-22 12:23:13 -0500 |
| commit | 2dd3aed6971fe74915c1fc09b4de20a6c64e817b (patch) | |
| tree | f9d896ebcfd0e6a2acb2684ff085a42324a9a638 | |
| parent | 5d18558615a127640d6b4e634997ee35a5fe1b77 (diff) | |
New Custom Functions, Org Updates, Agenda Updates, &c.
=== Functions ===
- add function cj/dired-convert-image-to-jpeg
- add function cj/dired-copy-path-as-kill
- add function cj/export-org-to-md-on-save function
- add function cj/org-link-to-current-file
=== Agenda ===
- rework the f8 main agenda into custom agenda using Aaron Bieber's ideas
- agenda lists now use standard agenda functions
- updated packages to remove org-super-agenda and ts (a dependency)
- file commentary text updated to reflect the change
=== Org ===
- add grocery and shopping list capture items
- add missing tests for org-skip-subtree-if-habit
- add support for org-habits, including checkboxes
- fix issue with org-auto-export-to-md
- modularize org-agenda
- move todo settings to org-config
- reformat org-roam-config
=== &c. ===
- add day to sortable date
- add disablefastrenderer to hugo server flags
- add fill column indicator when emacs lisping
- add gpt buffers to the bury-alive-list
- add keybinding for fixup-whitespace
- add magit cloning settings
- finish automatically on "Link" capture
- fix company complaints about shutting up function
- fix em, e, ff, and f eshell aliases
- make fallback theme modus-vivendi
- prune the compilation cache when applicable
27 files changed, 1994 insertions, 470 deletions
@@ -1,7 +1,571 @@ -g + === Emacs Config Tasks === * Emacs Config Inbox +** Emacs: modern minibuffer packages (Vertico, Consult, etc.) - Emacs - System Crafters +[[https://forum.systemcrafters.net/t/emacs-modern-minibuffer-packages-vertico-consult-etc/294][Emacs: modern minibuffer packages (Vertico, Consult, etc.) - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:46] +** Using auto-fill-mode vs. visual-line-mode - Emacs - System Crafters +[[https://forum.systemcrafters.net/t/using-auto-fill-mode-vs-visual-line-mode/275/3][Using auto-fill-mode vs. visual-line-mode - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:46] +** Check which version of the refile advice-add you have +#+BEGIN_QUOTE +I had this bit of code I got from one of Dave’s streams to save all buffers after refiling so you wouldn’t have to save each buffer by hand. + +(advice-add 'org-refile :after 'org-save-all-org-buffers) +Unfortunately, with org-ql, it gives an error. (It still refiles though). + +So, I found the solution here: + +emacs.stackexchange.com + +Org-mode: Getting errors when auto-saving after refiling 2 +org-mode +asked by wiuah on 09:50PM - 07 Sep 16 UTC +Which is this: + +(advice-add 'org-refile :after + (lambda (&rest _) + (org-save-all-org-buffers))) +I tested it with org-ql refiling and it does work perfectly. So any of you who were using that previous code and have issues with org-ql refiling, you can replace it with this and it should work. +#+END_QUOTE +[[https://forum.systemcrafters.net/t/save-all-buffers-on-org-refile/292][Save all buffers on org refile - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:45] +** Thread: Close all refile targets in ibuffer after refiling - Emacs - System Crafters +#+BEGIN_QUOTE +Close all refile targets in ibuffer after refiling +Emacs +Feb 15 +1 / 3 +Feb 15 +Feb 15 + + + +summeremacs + +1 +Feb 15 +Hi everyone + +Okay, so I have this problem where I always had a few hundred buffers open in ibuffer, and I couldn’t figure out what was doing it - since YEARS. Well, I finally figured it out the other day! Org refiling was the culprit. See, I didn’t know this but whenever you refile, when it says getting all targets, it’s opening them up (at least it did in my ibuffer behind the scenes). + +To counter this, I asked an AI to help me, and it did. It wrote the code but it couldn’t get the last part right about not opening ibuffer to kill everything. I fixed that up myself. So this is a collaboration between an AI and me, but the code works. + +The only “issue” now is that if you have a lot of targets, it will take a few seconds each time to do the gathering part. For me, that’s not a big deal, but maybe it is for you. And if you’re on here, there is a high likelihood of you being able to fix it so this becomes even better and faster. So here is my code. Please use it as you want. And if you can make it better, or faster somehow for repeated steps, then let me know (the closing part isn’t an issue - that takes a second on my machine, or less). + +(Note that the comment says that it’s bound to C-z o r but that isn’t shown in the code. It’s at the bottom of my config in custom key binds). + +;; This function uses C-z o r (listed at the end of config) to refile. It opens all the refile targets, and then closes them after refiling, so it keeps ibuffer clean. + (defun my/org-refile-and-close-buffers () + "Refile using org-refile and close related buffers in ibuffer." + (interactive) + ;; Get the list of open buffers before the refile + (let ((before-buffers (buffer-list)) + (org-refile-use-outline-path nil)) ; Disable outline path + (call-interactively 'org-refile) ; Call org-refile interactively + + ;; Get the list of open buffers after the refile + (let ((after-buffers (buffer-list))) + ;; Close only the buffers opened during the refile + (dolist (buffer before-buffers) + (setq after-buffers (delete buffer after-buffers))) + (mapc 'kill-buffer after-buffers)))) +I hope this helps! Now you can keep your ibuffer clean when you refile! + + + + + + +#+END_QUOTE +[[https://forum.systemcrafters.net/t/close-all-refile-targets-in-ibuffer-after-refiling/280][Close all refile targets in ibuffer after refiling - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:40] +** armindarvish/consult-mu: Consult Mu4e asynchronously in GNU Emacs +[[https://github.com/armindarvish/consult-mu][armindarvish/consult-mu: Consult Mu4e asynchronously in GNU Emacs]] +Captured On: [2024-04-19 Fri 05:33] +** Comment on Why Emacs will NEVER be popular! - Emacs - System Crafters +#+BEGIN_QUOTE +Emacs has a vibrant and active community that engages its entire member base. Furthermore, new mediums for outreach (such as Systemcrafters) are attracting newer, younger members, so it’s a base that is replenishing. What is the appeal to moving beyond that? + +Ease-of-use oriented distros could be double-edged. If we have a distro that’s intuitive and easy to use for the causal notepad/word processor user, that’s almost certain to widen the appeal of Emacs, and consequently increase its user-base. On the other hand, these people are going to be coming to Emacs for very different reasons than current Emacs users. Even if at first the new crowd is limited to a distro, if they influence the popularity of Emacs to the degree that it’s noticeable I don’t see how it won’t eventually influence the main editor itself. Outreach, concerted inclusion, and continued appeal to these folks will likely change the future trajectory that the editor takes. I’m not sure that’s a good thing. The things I like about Emacs are all distinctive from the alternatives out there, but I’m not sure those things would matter as much to the new crowd. After all, that’s not why they’re here. + +I do think distros have their place. Doom and Spacemacs (and Rational? Haven’t tried it in a while) strike a happy medium – they make it appealing to folks who want to give it a go, but they don’t change it to such a degree that those folks who try it and stick around have an alien perspective on what Emacs should be. +#+END_QUOTE +[[https://forum.systemcrafters.net/t/why-emacs-will-never-be-popular/432/7][Why Emacs will NEVER be popular! - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:32] +** alphapapa/listen.el: Audio/music player for Emacs +[[https://github.com/alphapapa/listen.el][alphapapa/listen.el: Audio/music player for Emacs]] +Captured On: [2024-04-19 Fri 05:28] +** alphapapa's flyspell setup +#+BEGIN_QUOTE +(use-package flyspell + :hook (org-mode . flyspell-mode) + :config + (define-advice flyspell-goto-next-error + (:around (oldfun &optional previous) ap/flyspell-goto-next-error) + "Go to next or previous misspelled word, or to previous position. +When no misspellings remain, goes to the position before +`flyspell-goto-next-error' was called." + (cl-labels ((next-error-pos (&optional previous) + (save-excursion + (pcase (funcall oldfun previous) + ("No more miss-spelled words" nil) + (_ (point)))))) + (if-let ((pos (or (next-error-pos) + (next-error-pos 'previous)))) + (progn + (pcase last-command + ((or 'flyspell-auto-correct-word 'flyspell-goto-next-error) + nil) + (_ (push-mark))) + (goto-char pos)) + (goto-char (mark-marker)) + (pop-mark))))) +#+END_QUOTE +[[https://forum.systemcrafters.net/t/emacs-config-request-for-feedback/425/4][Emacs config, request for feedback - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:23] +** Tips to investigate +#+BEGIN_QUOTE +For Org, I recommend installing org-ql and using org-ql-find as your primary way to find things in Org files. +I recommend using my new activities package. It makes it much easier to switch to and resume tasks that you do repeatedly in Emacs. +#+END_QUOTE +[[https://forum.systemcrafters.net/t/emacs-config-request-for-feedback/425][Emacs config, request for feedback - Emacs - System Crafters]] +Captured On: [2024-04-19 Fri 05:23] +** mhayashi1120/Emacs-wgrep: Writable grep buffer and apply the changes to files +[[https://github.com/mhayashi1120/Emacs-wgrep][mhayashi1120/Emacs-wgrep: Writable grep buffer and apply the changes to files]] +Captured On: [2024-04-19 Fri 05:08] +** Wilfred/deadgrep: fast, friendly searching with ripgrep and Emacs +[[https://github.com/Wilfred/deadgrep][Wilfred/deadgrep: fast, friendly searching with ripgrep and Emacs]] +Captured On: [2024-04-19 Fri 05:08] +** check this versus google this +#+BEGIN_QUOTE +(use-package search-web + :general + (bmj-leader-keys + "sw" '(:ignore t :which-key "web") + "sww" '(search-web :which-key "search") + "swd" '(search-web-dwim :which-key "dwim") + "swr" '(search-web-region :which-key "region"))) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-search.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-search.el - sourcehut git]] +Captured On: [2024-04-19 Fri 05:07] +** idle highlight mode instead of your hl setup? +#+BEGIN_QUOTE +(use-package idle-highlight-mode + :hook (prog-mode . idle-highlight-mode) + :config + (setopt idle-highlight-idle-time 0.5) + (defface idle-highlight '((t :inherit highlight :underline t)) + "Face used to highlight other occurrences of the word at point.")) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang.el - sourcehut git]] +Captured On: [2024-04-19 Fri 05:05] +** beniot's flycheck setup +note that he's putting the display-buffer-alist in the init section +#+BEGIN_QUOTE +(use-package flycheck + :init + (add-to-list 'display-buffer-alist + `(,(rx bos "*Flycheck errors*" eos) + (display-buffer-reuse-window + display-buffer-in-side-window) + (side . bottom) + (reusable-frames . visible) + (window-height . 0.15))) + :config + (global-flycheck-mode) + (evil-set-initial-state 'flycheck 'emacs)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang.el - sourcehut git]] +Captured On: [2024-04-19 Fri 05:04] +** beniot's org-download config +#+BEGIN_QUOTE +(use-package org-download + :after org + :general + (bmj-local-leader-keys + :states '(normal insert) + :keymaps 'org-mode-map + "i" '(:ignore t :which-key "insert") + "id" '(:ignore t :which-key "download") + "idu" '(org-download-yank :which-key "url") + "idd" '(org-download-clipboard :which-key "clipboard")) + + :config + (customize-set-variable 'org-download-method 'attach) + ;; (customize-set-variable 'org-download-backend "curl") + + (add-hook 'dired-mode-hook 'org-download-enable)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang-org.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-org.el - sourcehut git]] +Captured On: [2024-04-19 Fri 05:02] +** abo-abo/org-download: Drag and drop images to Emacs org-mode +[[https://github.com/abo-abo/org-download][abo-abo/org-download: Drag and drop images to Emacs org-mode]] +Captured On: [2024-04-19 Fri 05:01] +** awth13/org-appear: Toggle visibility of hidden Org mode element parts upon entering and leaving an element +[[https://github.com/awth13/org-appear][awth13/org-appear: Toggle visibility of hidden Org mode element parts upon entering and leaving an element]] +Captured On: [2024-04-19 Fri 04:59] +** beniot's lsp and dap setup +#+BEGIN_QUOTE +(use-package lsp-mode + :commands (lsp lsp-deferred) + :hook ((lsp-mode . lsp-enable-which-key-integration) + (lsp-mode . lsp-lens-mode)) + :bind + (:map lsp-mode-map + (("M-RET" . lsp-execute-code-action))) + :general + (bmj-leader-keys + "cl" '(:keymap lsp-command-map :which-key "lsp")) + :custom + (lsp-highlight-symbol-at-point nil) ; delegated to idle-highlight + (lsp-eldoc-render-all nil) + :init + (setq lsp-keymap-prefix "C-c l")) + +(use-package lsp-ui + :commands lsp-ui-mode + :custom + (lsp-ui-doc-delay 5.0) + (lsp-ui-sideline-enable t) + (lsp-ui-sideline-show-symbol nil)) + +(use-package lsp-treemacs :commands lsp-treemacs-errors-list) + +(use-package dap-mode + :after lsp-mode +;; :custom +;; (dap-java-terminal 'integratedTerminal) +;; (dap-internal-terminal #'dap-internal-terminal-vterm) + :config + (dap-auto-configure-mode)) + +(use-package consult-lsp + :general + (general-define-key :keymaps 'lsp-mode-map [remap xref-find-apropos] #'consult-lsp-symbols)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang-lsp.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-lsp.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:56] +** ~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-js-ts.el - sourcehut git +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang-js-ts.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-js-ts.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:54] +** beniot setting up editor configuration for a language +#+BEGIN_QUOTE +(defun bmj-default-java-code-style-hook () + "My default editor configuration for java." + (setq c-basic-offset 2 + c-label-offset 0 + tab-width 2 + indent-tabs-mode nil + compile-command "mvn -q -o verify" + require-final-newline nil)) + +(add-hook 'java-mode-hook 'bmj-default-java-code-style-hook) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang-java.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-java.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:53] +** setup devdocs context when coding +#+BEGIN_QUOTE + (add-hook 'emacs-lisp-mode-hook + (lambda () (setq-local devdocs-current-docs '("elisp")))) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-lang-el.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-lang-el.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:51] +** look into new shortdoc feature + +Version 28 of emacs introduced a new feature called shortdoc. It gives an overview over available elisp functions in a few select categories. Use the command M-x shortdoc-display-group and then type in which category you want to view and a buffer pops up giving you a brief overview. + +#+BEGIN_QUOTE +;; TODO we can generate our own cheatsheet with this +;; see https://www.masteringemacs.org/article/emacs-builtin-elisp-cheat-sheet +(use-package shortdoc + :ensure nil + :straight nil + :config + (evil-set-initial-state 'shortdoc 'emacs)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-help.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-help.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:49] +** does explain pause mode even work? +beniot has it in his config +#+BEGIN_QUOTE +(use-package explain-pause-mode + :general + (bmj-leader-keys + "ze" '(:ignore t :which-key "profile") + "zet" '(explain-pause-mode :which-key "toggle") + "zee" '(explain-pause-top :which-key "top")) + :config + (evil-set-initial-state 'explain-pause 'emacs)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-help.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-help.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:47] +** example of putting emacs files in the ~/.cache +#+BEGIN_QUOTE +(use-package undo-tree + ;; TODO: find a better way to defer it + :diminish undo-tree-mode + :demand t + :custom + (undo-tree-history-directory-alist '(("." . "~/.cache/emacs/undo"))) + :config + (global-undo-tree-mode)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-editor.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-editor.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:46] +** setup autoinsert for .el files for header/footer +#+BEGIN_QUOTE +(use-package autoinsert + :straight nil + :ensure nil + :demand t + :config + (setq auto-insert-directory (expand-file-name "~/.config/emacs/auto-insert/")) + (add-to-list 'auto-insert-alist '("shell\\.nix" . "shell.nix")) + (auto-insert-mode 1)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-editor.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-editor.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:43] +** beniot's selection framework setup +#+BEGIN_QUOTE +(use-package vertico + :demand t + :config + (evil-set-initial-state 'vertico 'emacs) + (setopt vertico-cycle t) + (vertico-mode 1)) + +;; this works also for CAP +(use-package orderless + :demand t + :config + ;; Define orderless style with initialism by default + (orderless-define-completion-style +orderless-with-initialism + (orderless-matching-styles '(orderless-initialism orderless-literal orderless-regexp))) + + (setq completion-styles '(orderless basic) + completion-category-overrides '((file (styles basic partial-completion)) + ;; enable initialism by default for symbols + ; (command (styles +orderless-with-initialism)) + ; (variable (styles +orderless-with-initialism)) + ; (symbol (styles +orderless-with-initialism)) + ) + orderless-matching-styles '(orderless-literal orderless-initialism orderless-regexp ;; orderless-flex + ) + orderless-component-separator #'orderless-escapable-split-on-space ;; allow escaping space with backslash! + )) + +(use-package marginalia + :hook (minibuffer-mode . marginalia-mode) + :custom + (marginalia-annotators '(marginalia-annotators-heavy + marginalia-annotators-light + nil)) + :config + (add-to-list 'marginalia-annotator-registry + '(symbol-help marginalia-annotate-variable) + '(buffer marginalia-annotate-buffer)) + (add-to-list 'marginalia-prompt-categories '("Buffer: " . buffer)) + (marginalia-mode 1)) + + +(use-package consult + :general + (general-define-key + [remap recentf-open-files] #'consult-recent-file + [remap bookmark-jump] #'consult-bookmark + [remap evil-show-marks] #'consult-mark + [remap evil-show-jumps] #'+vertico/jump-list + [remap goto-line] #'consult-goto-line + [remap imenu] #'consult-imenu + [remap locate] #'consult-locate + [remap load-theme] #'consult-theme + ;; TODO consult-man does not work + [remap man] #'consult-man + [remap switch-to-buffer-other-window] #'consult-buffer-other-window + [remap switch-to-buffer-other-frame] #'consult-buffer-other-frame + [remap yank-pop] #'consult-yank-pop + [remap isearch-forward] #'consult-line + ;; [remap evil-redo] + ;; #'consult-history + ) + (bmj-leader-keys + "ss" '(consult-ripgrep :which-key "ripgrep")) + :config + (setq completion-in-region-function #'consult-completion-in-region + consult-async-min-input 1) + (evil-set-initial-state 'consult 'emacs)) + +;; TODO: https://karthinks.com/software/fifteen-ways-to-use-embark/ + +(use-package embark + :general + (general-define-key + [remap describe-bindings] #'embark-bindings) + + :bind + (("C-." . embark-act) + ("C-," . embark-export)) + ;; TODO conflict with iedit + ;("C-;" . embark-dwim)) + + :config + (evil-set-initial-state 'embark 'emacs) + (evil-set-initial-state 'vertico 'emacs) + + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +;; allow to export grep results into a occur like buffer +(use-package embark-consult + :hook ((embark-collect-mode . consult-preview-at-point-mode) + (minibuffer-mode . (lambda () nil)))) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-completion.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-completion.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:39] +** alphapapa/hammy.el: Programmable, interactive interval timers (e.g. for working/resting) +[[https://github.com/alphapapa/hammy.el][alphapapa/hammy.el: Programmable, interactive interval timers (e.g. for working/resting)]] +Captured On: [2024-04-19 Fri 04:36] +** SqrtMinusOne/pomm.el: Implementation of Pomodoro and Third Time techniques for Emacs +[[https://github.com/SqrtMinusOne/pomm.el][SqrtMinusOne/pomm.el: Implementation of Pomodoro and Third Time techniques for Emacs]] +Captured On: [2024-04-19 Fri 04:34] +** wbolster/emacs-direnv: direnv integration for emacs +[[https://github.com/wbolster/emacs-direnv][wbolster/emacs-direnv: direnv integration for emacs]] +Captured On: [2024-04-19 Fri 04:31] +** emacsmirror/0x0: Upload sharing to 0x0.st + +Easily share images on erc, etc. +[[https://github.com/emacsmirror/0x0][emacsmirror/0x0: Upload sharing to 0x0.st]] +Captured On: [2024-04-19 Fri 04:28] +** walseb/blimp: A complete wrapper around all imagemagick commands with autocompletion, descriptions and hints displayed in prompt +[[https://github.com/walseb/blimp][walseb/blimp: A complete wrapper around all imagemagick commands with autocompletion, descriptions and hints displayed in prompt]] +Captured On: [2024-04-19 Fri 04:27] +** simple music setup to consider +#+BEGIN_QUOTE +(use-package mpc + :commands (mpc)) + +(use-package mpdel + :general + (bmj-leader-keys + "aM" '(mpdel-browser-open :which-key "mpdel"))) + +(use-package simple-mpc) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-app-media.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-app-media.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:25] +** Investigate disk-usage and trashed +#+BEGIN_QUOTE +(use-package disk-usage + :general + (bmj-leader-keys + "fu" '(:ignore t :which-key "disk usage") + "fuu" '(disk-usage :which-key "somewhere") + "fu." '(disk-usage :which-key "here") + "fup" '((lambda () disk-usage (project-root (project-current))) :which-key "project")) + :config + (evil-set-initial-state 'disk-usage 'emacs) + ) + +(use-package trashed + :general + (bmj-leader-keys + "ft" '(trashed :which-key "trashed")) + :config + (evil-set-initial-state 'trashed 'emacs)) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-app-file-mgmt.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-app-file-mgmt.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:23] +** rest client config from beniot +#+BEGIN_QUOTE +(eval-when-compile + (require 'cl)) + +(use-package restclient + :straight + (restclient :type git :host github + :repo "pashky/restclient.el" + :files ("restclient.el" "restclient-jq.el")) + :mode (("\\.rest\\'" . restclient-mode)) + :config + (require 'restclient-jq) + (evil-set-initial-state 'restclient 'emacs)) + +(use-package restclient-test + :hook (restclient-mode . restclient-test-mode) + :general + (bmj-local-leader-keys + :states '(normal insert) + :keymaps 'restclient-mode-map + "t" '(:ignore t :which-key "test") + "tt" '(restclient-test-buffer :which-key "buffer") + "tc" '(restclient-test-current :which-key "current"))) + +(use-package plz) +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-app-http.el][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-app-http.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:22] +** 10sr/git-walktree-el: Browse Git Tree and Blob Objects +[[https://github.com/10sr/git-walktree-el][10sr/git-walktree-el: Browse Git Tree and Blob Objects]] +Captured On: [2024-04-19 Fri 04:20] +** Mastodon Packages and setup from benoit +#+BEGIN_QUOTE +(use-package mastodon + :commands (mastodon) + :general + (bmj-leader-keys + "ax" '(mastodon :which-key "mastodon")) + :config + (setopt mastodon-instance-url "https://fosstodon.org") + (setopt mastodon-active-user "@benoitj") + (setopt mastodon-auth-source-file "~/.authinfo.gpg") + (setopt mastodon-tl--show-avatars t) + (setopt mastodon-media--avatar-height 50) + (mastodon-alt-tl-activate) + (mastodon-async-mode)) + + +(use-package mastodon-alt + :demand t + :after (mastodon) + :straight (mastodon-alt :host github + :repo "rougier/mastodon-alt") + :commands (mastodon-alt-tl-activate + mastodon-alt-tl-deactivate)) + +#+END_QUOTE +[[https://git.sr.ht/~benoit/dotfiles/tree/main/item/home/dotfiles/config/emacs/modules/config-app-social.el#L84][~benoit/dotfiles (main): home/dotfiles/config/emacs/modules/config-app-social.el - sourcehut git]] +Captured On: [2024-04-19 Fri 04:09] +** Tiny Tiny RSS +[[https://tt-rss.org/][Tiny Tiny RSS]] +Captured On: [2024-04-19 Fri 04:04] +** fasheng/elfeed-protocol: make standalone RSS aggregators work with elfeed +[[https://github.com/fasheng/elfeed-protocol?tab=readme-ov-file][fasheng/elfeed-protocol: Provide extra protocols to make like Fever, NewsBlur, Nextcloud/ownCloud News and Tiny Tiny RSS work with elfeed]] +Captured On: [2024-04-19 Fri 04:03] +** TODO Create a fontaine set for more Serif fonts. +- Georgia +- Garamond +** sprig/org-capture-extension: A Chrome and firefox extension facilitating org-capture in emacs +** TODO Add this grep to org-drill +#+BEGIN_QUOTE +how would I use grep to search recursively for any instance of "epdfinfo" in files called todo.org where the filename could also be TODO.org (case insensitive)? + +You can use =grep= with the =-r= (or =--recursive=) option to search recursively, and the =-i= (or =--ignore-case=) option to ignore case. The =-E= option allows you to use extended regular expressions, which is especially helpful when searching case-insensitive extension. Here's how you can do it: + +#+begin_src bash + grep -ri --include=\*.[Tt][Oo][Dd][Oo].[Oo][Rr][Gg] 'epdfinfo' . +#+end_src + +This will search for the term "epdfinfo" in all files named =todo.org= or =TODO.org= (or any other case variations like =Todo.org=, =tOdO.org=, etc.) in the current directory (denoted by =.=) and its subdirectories. + +Please note that =-i= is case-insensitive for the searched term, not for the file extension. We use =[Tt][Oo][Dd][Oo].[Oo][Rr][Gg]= to manually handle case-insensitive file extensions. +#+END_QUOTE +[[][]] +Captured On: [2024-04-18 Thu 06:15] * DOING Emacs Config v0.7 [2/5] ** DONE Supplement Existing Functionality [9/9] *** DONE [#B] Make sure eshell has same path as other shells @@ -140,8 +704,44 @@ This is the result of overriding that function in cj/invincible-buffers in syste *** DONE [#C] Bookmark-save-flag Replaces Bookmark-set-and-save CLOSED: [2024-04-03 Wed 15:00] remove cj/bookmark-set-and-save if setting bookmark-save-flat to 1 saves when adding/modifying a bookmark -** DOING Open v0.7 Fit and Finish (ends 2024.04.18) [1/7] -*** TODO [#B] Save All Attachments Working in Mu4e +** DOING Open v0.7 Fit and Finish (ends 2024.04.20) [1/6] +*** DOING [#B] Modularize all org-mode config files into use-package statements +- [X] org-config +- [X] org-agenda +- [ ] org-roam +- [ ] org-appearance +- [ ] org-babel +- [ ] org-capture +- [ ] org-refile +- [ ] org-contacts +- [ ] org-drill +- [ ] org-export +*** DOING [#B] Save All Attachments Working in Mu4e +**** 2024-04-18 Thu @ 05:55:32 -0500 The Documentation Suggests A Path Forward +This suggests that if I'm writing another function, I should +- first query the user to select/create the directory +- make sure mu4e-view-completion-minor-mode is on +- call mu4e-view-complete-all to get all the files +- within the context of mu4e-view-save-attachments + +#+BEGIN_QUOTE +E-mail messages can be though as a series of “MIME-parts”, which are sections of the message. The most prominent is the ’body’, that is the main message your are reading. Many e-mail messages also contains attachments, which MIME-parts that contain files10. + +To save such attachments as files on your file systems, the mu4e message-view offers the command mu4e-view-save-attachments; default keybinding is e (think extract). After invoking the command, you can enter the file names to save, comma-separated, and using the completion support. Press RET to save the chosen files to your file-system. + +With a prefix argument, you get to choose the target-directory, otherwise, mu4e determines it following the variable mu4e-attachment-dir (which can be file-system path or a function; see its docstring for details. + +While completing, mu4e-view-completion-minor-mode is active, which offers mu4e-view-complete-all (bound to C-c C-a to complete all files11. +#+END_QUOTE +[[https://www.djcbsoftware.nl/code/mu/mu4e/MSGV-Attachments-and-MIME_002dparts.html][MSGV Attachments and MIME-parts (Mu4e 1.12.3 user manual)]] + +**** 2024-04-18 Thu @ 05:05:03 -0500 Etienne's config failed +It relies on this line to find all the mime-parts: + +(let ((parts (mu4e--view-gather-mime-parts)) + +and that doesn't exist any longer. +**** Test Etienne's Configuration https://etienne.depar.is/emacs.d/mu4e.html (defun ed/mu4e-view-save-all-attachments (&optional arg) "Save all attachments of a given message. @@ -185,8 +785,8 @@ choose a specific directory where to save all the files." file)))) (mu4e-message "No attached files found"))))) (define-key mu4e-view-mode-map "X" #'ed/mu4e-view-save-all-attachments) -*** TODO [#B] Get queued email working with this msmtp feature -**** using sendmail +*** TODO [#B] Get queued email working for both mail accounts +**** using sendmail for gmail #+BEGIN_QUOTE Queuing mails for sending it later Mu4e supports a very nice feature which is mail queueing from smtpmail emacs client. To enable it, it requires two easy steps: @@ -207,7 +807,7 @@ Note: there is a bug (not sure it’s really a bug). When sending a mail into th [[https://dataswamp.org/~solene/2018-05-22-mu4esmtp.html][Solene'% : Sending mail with mu4e]] Captured On: [2024-04-13 Sat 05:23] -**** with msmtp +**** with msmtp for cmail on archwiki: https://wiki.archlinux.org/title/msmtp#Using_msmtp_offline (read the whole page anyway) also here: https://www.reddit.com/r/emacs/comments/q2gl5h/queuing_emails_and_sending_them_autmatically/ @@ -226,25 +826,6 @@ Adding /usr/local/bin to your PATH can save you some keystrokes if you are doing #+END_QUOTE [[https://wiki.archlinux.org/title/msmtp#Using_msmtp_offline][msmtp - ArchWiki]] Captured On: [2024-04-13 Sat 09:35] -*** TODO [#B] Move org-todo items into org-config use-package statement -*** TODO [#B] Separate org-agenda items into use-package statement -add after: org-roam for the function references -*** TODO [#C] Articles Should Be Saved Under todo.org TO READ Heading -**** 2024-04-16 Tue @ 10:28:17 -0500 Notes -This involves changing webclipper. Webclipper as it is now doesn't return the content, it actually yanks it into the article-file. -What I want is something to return the content in the way that a template would expect it. This way, I can create templates that would work with org-roam as well, such as creating a separate org-roam node just by clicking an org-protocol bookmarklet. - -This might be too large to fit as a bug fix for v0.7. I began doing some research (below) yesterday to help me size the work. -**** 2024-04-16 Tue @ 10:27:25 -0500 Research -***** TODO read through capturing content for emacs -[[https://howardism.org/Technical/Emacs/capturing-content.html][Capturing Content for Emacs]] -Captured On: [2024-04-15 Mon 22:27] -***** TODO Investigate Org Mode's Web Archiver -[[https://github.com/charlesroelli/org-board][charlesroelli/org-board: Org mode's web archiver.]] -Captured On: [2024-04-15 Mon 22:33] -***** TODO Review these Web Page tools for Org-mode -[[https://github.com/alphapapa/org-web-tools][alphapapa/org-web-tools: View, capture, and archive Web pages in Org-mode]] -Captured On: [2024-04-15 Mon 22:55] *** DOING [#C] Remove all unnecessarily hardcoded filenames and paths **** DONE Fix hardcoded in org-roam @@ -340,7 +921,23 @@ Captured On: [2024-04-15 Mon 22:55] ./modules/calibredb-epub-config.el:27: (setq calibredb-root-dir "~/sync/books/") ./modules/calibredb-epub-config.el:28: (setq calibredb-library-alist '(("~/sync/books/"))) ./modules/elfeed-config.el:129: (let ((default-directory "~/videos")) -*** [#D] Resolved v0.7 Fit and Finish Work [16/16] +*** TODO [#C] Articles Should Be Saved Under todo.org TO READ Heading +**** 2024-04-16 Tue @ 10:28:17 -0500 Notes +This involves changing webclipper. Webclipper as it is now doesn't return the content, it actually yanks it into the article-file. +What I want is something to return the content in the way that a template would expect it. This way, I can create templates that would work with org-roam as well, such as creating a separate org-roam node just by clicking an org-protocol bookmarklet. + +This might be too large to fit as a bug fix for v0.7. I began doing some research (below) yesterday to help me size the work. +**** 2024-04-16 Tue @ 10:27:25 -0500 Research +***** TODO read through capturing content for emacs +[[https://howardism.org/Technical/Emacs/capturing-content.html][Capturing Content for Emacs]] +Captured On: [2024-04-15 Mon 22:27] +***** TODO Investigate Org Mode's Web Archiver +[[https://github.com/charlesroelli/org-board][charlesroelli/org-board: Org mode's web archiver.]] +Captured On: [2024-04-15 Mon 22:33] +***** TODO Review these Web Page tools for Org-mode +[[https://github.com/alphapapa/org-web-tools][alphapapa/org-web-tools: View, capture, and archive Web pages in Org-mode]] +Captured On: [2024-04-15 Mon 22:55] +*** [#D] Resolved v0.7 Fit and Finish Work [25/25] **** DONE [#C] Theme Doesn't Fallback on Wombat Correctly if No Theme Persist File **** DONE [#A] Remove Keys CLOSED: [2024-04-07 Sun 13:46] @@ -488,6 +1085,84 @@ CLOSED: [2024-04-16 Tue 11:46] It's a bit crazy that I spend time each morning organizing and scheduling my day, then when the time comes, no notification happens. This is a bug fix, only insofar as it's fixing essentially broken in the Emacs workflow, but it's additional functionality. I'm hoping this is straightforward and I can spend a day on it to get this working, then move on to other bug fixes. +**** DONE [#B] Show kill ring no longer works +CLOSED: [2024-04-18 Thu 05:39] +**** DONE [#B] Move org-todo items into org-config use-package statement +CLOSED: [2024-04-18 Thu 06:01] +**** DONE [#C] Fix company error message +CLOSED: [2024-04-18 Thu 06:03] +This is because I removed the shut-up function earlier but failed to remove this particular use of it. +Now removed, so closing +***** What's the Issue? +Company: An error occurred in auto-begin +Company: backend company-capf error "Symbol’s function definition is void: shut-up" with args (prefix) +**** DONE [#B] Rework org agenda +CLOSED: [2024-04-18 Thu 20:26] +:LOGBOOK: +- State "DONE" from "PROJECT" [2024-04-18 Thu 20:26] +- State "TODO" from [2024-04-18 Thu 12:35] +:END: +***** DONE [#B] Create All Tasks List +CLOSED: [2024-04-18 Thu 20:25] +:LOGBOOK: +- State "DONE" from "TODO" [2024-04-18 Thu 20:25] +- State "TODO" from [2024-04-18 Thu 16:42] +:END: + +***** DONE [#B] Create All Tasks Current Buffer List +CLOSED: [2024-04-18 Thu 20:25] +:LOGBOOK: +- State "DONE" from "TODO" [2024-04-18 Thu 20:25] +- State "TODO" from [2024-04-18 Thu 16:43] +:END: +***** DONE [#B] Remove Org-Super-Agenda Code and Test +CLOSED: [2024-04-18 Thu 20:26] +:LOGBOOK: +- State "DONE" from "TODO" [2024-04-18 Thu 20:26] +- State "TODO" from [2024-04-18 Thu 17:02] +:END: +To do after all other tasks have been created +- [X] Quick search for super agenda methods that need removing +- [X] Remove org-super-agenda +- [X] Test all various f8 functions work +- [X] Remove org-super-agenda from elpa + Reboot to ensure it's not reinstalled +- [X] Regenerate localrepo after restarting + This ensures any dependencies are gone +***** DONE [#B] Reword Commentary for Accuracy +CLOSED: [2024-04-18 Thu 20:26] +:LOGBOOK: +- State "DONE" from "TODO" [2024-04-18 Thu 20:26] +- State "TODO" from [2024-04-18 Thu 18:06] +:END: + +***** DONE [#B] Create Main Agenda +CLOSED: [2024-04-18 Thu 17:02] +:LOGBOOK: +- State "DONE" from "DOING" [2024-04-18 Thu 17:02] +- State "DOING" from "TODO" [2024-04-18 Thu 16:41] +- State "TODO" from [2024-04-18 Thu 16:40] +:END: +Bind it and test all custom functions used. +***** 2024-04-18 Thu @ 12:42:31 -0500 Project Scope +This is with an eye to remove org-super-agenda. +I really just need: +- an agenda with all pri A tasks, the week's schedule, all B & C tasks + this should be bound to f8 +- all tasks and projects + bound to C-f8 +- all tasks and projects for this buffer + bound to M-f8 + +**** DONE [#C] Fix Simple Task Capture +CLOSED: [2024-04-18 Thu 22:14] +:LOGBOOK: +- State "DONE" from "TODO" [2024-04-18 Thu 22:14] +:END: +Now mapped to C-S-t at the bottom of org-capture-config.el +***** 2024-04-18 Thu @ 20:31:44 -0500 Issue +It's now failing with +org-roam-capture--fill-template: Wrong type argument: char-or-string-p, (expand-file-name (concat roam-dir "/inbox.org")) ** DOING Complete v0.7 Release Checklist [8/11] *** DOING 2 Week Fit and Finish (ends 2024.04.18) *** TODO Clean Launch from Archsetup @@ -500,7 +1175,7 @@ I'm hoping this is straightforward and I can spend a day on it to get this worki *** DONE Clean Launch from Git Clone *** DONE Merged Cleanly Into Main Branch *** DONE Main Branch Pushed to github and git.cjennings.net -* Emacs Config v0.8 +* PROJECT Emacs Config v0.8 ** TODO Hugo Blogging Workflow *** 2024-04-04 Thu @ 16:44:40 -0500 Thoughts on Functionality The whole blogging workflow should happen in Emacs. @@ -620,7 +1295,7 @@ Captured On: [2024-04-06 Sat 11:09] The number and complexity of bugs will tell me whether terminal Emacs is supported in this tag. ** TODO Emacs Config v0.8 Release Checklist (add latest release checklist here) -* Emacs Config v0.9 +* PROJECT Emacs Config v0.9 ** TODO VC Installs from Github ** TODO Prog Go Workflow ** TODO Prog Python Workflow @@ -634,7 +1309,7 @@ The number and complexity of bugs will tell me whether terminal Emacs is support - [ ] Main Branch Pushed to github and git.cjennings.net - [ ] 2 Weeks of Use and Bug Fixing - [ ] Mark Release as DONE and Create Tag on Repo -* Emacs Config v0.10 +* PROJECT Emacs Config v0.10 ** TODO Complete "Localrepo" Functionality *** 2024-04-04 Thu @ 21:11:04 -0500 Considering Renaming "Localrepo" I worry that "localrepo" will be confused with elpa-mirrors, both local and remote. @@ -2983,6 +3658,15 @@ All the following functions interact with an email headers to expose or find out #+END_QUOTE [[https://etienne.depar.is/emacs.d/mu4e.html][Mu4e customization]] Captured On: [2024-04-13 Sat 04:56] +*** TODO Ensure you can attach items to an email message using dired +#+BEGIN_QUOTE +It is possible to attach files to mu4e messages using dired (emacs), using the following steps (based on a post on the mu-discuss mailing list by Stephen Eglen). + +(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) +Then, mark the file(s) in dired you would like to attach and press C-c RET C-a, and you’ll be asked whether to attach them to an existing message, or create a new one. +#+END_QUOTE +[[https://www.djcbsoftware.nl/code/mu/mu4e/Dired.html][Dired (Mu4e 1.12.3 user manual)]] +Captured On: [2024-04-18 Thu 05:52] ** Emacs Markdown Config Backlog [0/1] *** TODO Finish Markdown Preview Functionality ** Emacs Modeline Backlog [0/3] @@ -3055,6 +3739,9 @@ org-caldav-sync-changes-to-org 'all) #+END_QUOTE [[https://github.com/DamienCassou/emacs.d/blob/master/init.el#L352][emacs.d/init.el at master · DamienCassou/emacs.d]] Captured On: [2024-04-05 Fri 15:24] +*** TODO This org-gcal appears to be more recent +[[https://github.com/kidd/org-gcal.el][kidd/org-gcal.el: Org sync with Google Calendar. (active maintained project as of 2019-11-06)]] +Captured On: [2024-04-18 Thu 08:27] ** Emacs Org Babel Backlog [0/3] *** TODO Add Org Babel Support for PGN [[https://gist.github.com/larrasket/4188cac7d0d034eda6ea99160e94dc57][Add org babel support for Portable Game Notation]] @@ -3078,6 +3765,9 @@ Captured On: [2023-11-01 Wed 12:34] - 'article-archive' should be 'saved-articles' - 'article-file' should be 'article-inbox' or something else +*** TODO Put a generic org capture on Capture Templates +This is for use when it's not a TODO item and you want to capture it via the browser. +Captured On: [2024-04-16 Tue 20:03] ** Emacs Org Drill Backlog [0/6] *** TODO Create Emacs Drill Questions From This Post About Adding to Lists #+BEGIN_QUOTE @@ -3345,6 +4035,9 @@ the following to register the mime-type with the OS: #+END_QUOTE [[https://www.reddit.com/r/emacs/comments/fvlcqg/anyone_using_orgprotocol_with_only_a_window/][(3) Anyone using org-protocol with only a window manager but no desktop environment? : emacs]] Captured On: [2024-04-02 Tue 16:54] +*** TODO Add refile mode to the right mu4e mail view mode +[[][]] +Captured On: [2024-04-16 Tue 20:04] ** Emacs Org Roam Backlog [0/5] *** TODO Compare Orgrr versus Org-Roam [[https://github.com/rtrppl/orgrr][GitHub - rtrppl/orgrr: Orgrr is a minimalist but complete note-taking system for Emacs. Its intended purpose is the creation and management of a Zettelkasten-like system.]] @@ -3423,6 +4116,12 @@ Captured On: [2023-10-21 Sat 08:41] *** TODO Look into Delve for Org-Roam [[https://github.com/publicimageltd/delve][publicimageltd/delve: Delve into your org-roam zettelkasten]] Captured On: [2024-04-05 Fri 13:57] +*** TODO Can't export from recipes due to formatting +Org mode needs a single all-encompassing header, otherwise it will just export that particular section on it's own. +If you can't find an easy workaround, you will need to: +- change all the current recipes +- set a property at the top of the buffer telling org to expand the top node so the buffer doesn't appear empty. +- change the template, including adding the property ** Emacs PDF Backlog [0/2] *** TODO Double Check PDF-tools Settings Across All Emacs Functionality [[https://emacs.stackexchange.com/questions/19686/how-to-use-pdf-tools-pdf-view-mode-in-emacs][gnus - How to use pdf-tools (pdf-view-mode) in emacs? - Emacs Stack Exchange]] @@ -4755,3 +5454,464 @@ When I run Emacs (with xwidget-webkit support enabled) like this: ~/.local/opt/emacs/bin/emacs --init-directory ~/.config/emacs -xrm "emacs.synchronous: true" xwidget-webkit works! +** Set up Emacs to work with gpg files for authentication +Article Link: http://prodissues.com/2016/02/emacs-gpg-for-dummies.html +Captured On: [2024-04-13 Sat 05:29] +[[http://prodissues.com/][prodissues]] + +• [[http://prodissues.com/about/][about]] +• [[http://prodissues.com/about/#contact][contact]] +• [[https://twitter.com/prodissues][twitter]] +• [[http://prodissues.com/feeds/all.atom.xml][feed]] + +Emacs GPG For Dummies + +Tue 16 February 2016 by yaniv + +I've [[https://prodissues.com/2016/02/adding-mu4e-support-to-emacs.html][set up mu4e]], and have my Gmail credentials stored in two files: + +1 .offlineimaprc - this file is used by Offlineimap to connect to my Gmail and sync my inbox with mu4e. +2 .authinfo - that file stores my Gmail credential, and used by Emacs to send emails. + +Unfortunately, both of those files are plain text, and though I’m not a security freak, I’m uncomfortable +storing my passwords out in the open. So, I went ahead to find out how to encrypt them. Most of the +tutorials I read were too technical, and covered much more than my simple usecase. It’s not that I +couldn’t follow theme, but I know I wouldn’t have retained the information, and able to retract my steps +if I needed to in the future. + +My goal with this post was to create a simple guide on how to install gpg, generate a key, and use it in +mu4e. I failed... I thought I will be able to it non-technical for the most part, but once getting to +configure Emacs and mu4e to work with gpg, I had to delve into some heavy configuration, which included +the creation of a python script to work along Offlineimap... The good thing is that this guide will help +you get Emacs and mu4e work with an encrypted version of a .authinfo file, and your credentials will remain +secret. + +Now that our expectations are set, and assuming you're up for the ride, lets start this journey. + +Installing GPG + +~ $ brew install gpg + +Let's make sure gpg was installed: + +~ $ gpg --version + +gpg--version.png + +Figure 1: gpg version information along with the list of supported algorithms + +Now really, the most informative source of information is gpg's help. Go ahead and skim it: + +~ $ gpg -h + +Create a key + +~ $ gpg --gen-key + +There’s a simple wizard that lets you set the encryption type, and asks for your name, email address and +other comments. Those details will be associated with your key. + +Next, you’ll be asked to create a passphrase. This is like the password to your secret key. If you lose +it, you’ll have no access to any of the information encrypted with this key. So don’t ever lose it… + +Here’s how this flow looks like: + +~ $ gpg --gen-key +gpg (GnuPG) 1.4.19; Copyright (C) 2015 Free Software Foundation, Inc. +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +Please select what kind of key you want: + (1) RSA and RSA (default) + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) +Your selection? 1 +RSA keys may be between 1024 and 4096 bits long. +What keysize do you want? (2048) +Requested keysize is 2048 bits +Please specify how long the key should be valid. + 0 = key does not expire + <n> = key expires in n days + <n>w = key expires in n weeks + <n>m = key expires in n months + <n>y = key expires in n years +Key is valid for? (0) +Key does not expire at all +Is this correct? (y/N) y + +You need a user ID to identify your key; the software constructs the user ID +from the Real Name, Comment and Email Address in this form: + "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>" + +Real name: Jane Roe +Email address: jane@example.com +Comment: lorem ipsum +You selected this USER-ID: + "Jane Roe (lorem ipsum) <jane@example.com>" + +Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O +You need a Passphrase to protect your secret key. + +We need to generate a lot of random bytes. It is a good idea to perform +some other action (type on the keyboard, move the mouse, utilize the +disks) during the prime generation; this gives the random number +generator a better chance to gain enough entropy. +..........+++++ +.+++++ +We need to generate a lot of random bytes. It is a good idea to perform +some other action (type on the keyboard, move the mouse, utilize the +disks) during the prime generation; this gives the random number +generator a better chance to gain enough entropy. +..........+++++ +...+++++ +gpg: key 86B62C98 marked as ultimately trusted +public and secret key created and signed. + +gpg: checking the trustdb +gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model +gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u +pub 2048R/86B62C98 2016-02-17 + Key fingerprint = 42FD C031 BD51 4CC8 7C02 EA14 35D4 80A2 86B6 2C98 +uid Jane Roe (lorem ipsum) <jane@example.com> +sub 2048R/8C0D5E5D 2016-02-17 + +~ $ + +Now that you've created a key, you can go ahead and sign the .authinfo file. + +Sign and encrypt the .authinfo file[[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fn-1][1]] + +~ $ gpg -se .authinfo + +You'll be asked for your passphrase. Enter it, and the .authinfo will be signed, and renamed to . +authinfo.gpg + +[[https://www.emacswiki.org/emacs/GnusAuthinfo][EmacsWiki]] suggests to limit permission to this file. I find it important: + +~ $ chmod 600 .authinfo.gpg + +Back in Emacs, there are couple of changes we need to make in order for mu4e to start working with the , +authinfo.gpg file. I wish I read [[https://gist.github.com/areina/3879626][this gist]] before, because it covers those changes succinctly, but here is +a summary of those modifications: + +Changes to .offlineimaprc + +Two additions: + +1 A reference to a python file where you'll store a function to fetch your credentials from the . + authinfo.gpg file +2 Under the [Repository Remote] section add the call to the get_password_emac function + +Here's how your .offlineimaprc file will look like afterwards: + +[general] +accounts = Gmail +maxsyncaccounts = 3 +pythonfile = ~/.offlineimap.py + +[Account Gmail] +localrepository = Local +remoterepository = Remote + +[Repository Local] +type = Maildir +localfolders = ~/Maildir +[Repository Remote] +type = IMAP +remoteuser = yanivdll@gmail.com +remotehost = imap.gmail.com +remotepasseval = get_password_emacs("imap.gmail.com", "yanivdll@gmail.com", "993") +ssl = yes +sslcacertfile = /usr/local/etc/openssl/certs/ca-bundle.crt +maxconnections = 1 +realdelete = no + +Add a .offlineimap.py file + +This file will define the get_password_emac function: + +#!/usr/bin/python +import re, os + +def get_password_emacs(machine, login, port): + s = "machine %s login %s port %s password ([^ ]*)\n" % (machine, login, port) + p = re.compile(s) + authinfo = os.popen("gpg -q --no-tty -d ~/.authinfo.gpg").read() + return p.search(authinfo).group(1) + +Changes to mu4e config + +------------------------------------------------------------------------------------------------------------------------- + +Lastly, in your Emacs config, under the mu4e smtp settings, add a reference to the encrypted auth file: + +... +(setq message-send-mail-function 'smtpmail-send-it + starttls-use-gnutls t + smtpmail-starttls-credentials + '(("smtp.gmail.com" 465 nil nil)) + smtpmail-auth-credentials + (expand-file-name "~/.authinfo.gpg") + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 465 + smtpmail-debug-info t) +... + +Now, you're emails should be sent using the .authinfo.gpg file. Go on and try it[[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fn-2][2]]. Note that before +actually sending the email, Emacs will ask for your pass-phrase[[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fn-3][3]] + +Extras + +Backup private key + +I stored all the information related to my gpg key, as well as a backup file in my [[https://agilebits.com/onepassword][1password]]. Here's how I +created the key backup: + +~ $ gpg --export-secret-keys --armor jane@example.com > jroe-privkey.asc + +Important: Make sure to store the output file in a secure place; it contains your private key in plain +text. + +Encrypt text in Emacs + +1 Mark the text you would like to encrypt +2 Run M-x epa-encrypt-region +3 Mark the key you would like to use for encryption + +Now the encrypted text will replace the original, plain, text: + +encrypted-text.png + +Figure 2: M-x epa-encrypt-region will encrypt a region of text in Emacs + +Decrypt text + +To decrypt a message, or a file you've encrypted: + +1 Mark the text you would like to decrypt (you'll have to mark also the header and footer of the message) +2 Run M-x epa-decrypt-region + +decrypt-text.png + + Figure 3: M-x epa-decrypt-region will decrypt a region of text in Emacs + +3 Enter your pass-phrase + +4 Emacs will ask if you want the decrypted text to replace the original text. If you choose "No", it will + open the text in a second window. + +decrypted-text-2.png + +Figure 4: The decrypted text in a second window + +That's it. If you're interested in more than the basics that I went through above, try the links bellow. + +Reference + +• [[https://www.gnupg.org/documentation/howtos.html][Gnupg - documentation]] +• Using gpg in emacs - [[https://www.gnu.org/software/emacs/manual/html_mono/epa.html#Quick-start][EasyPG Assistant user’s manual]] +• [[https://fedoraproject.org/wiki/Creating_GPG_Keys#ExportCLI][Fedora Wiki pages]] - GPG essentials +• [[https://www.emacswiki.org/emacs/GnusAuthinfo][EmacsWiki - GnusAuthinfo]] +• [[http://danzorx.tumblr.com/post/11976550618/easypg-for-emacs-on-os-x-or-sometimes-emacs][Tricotism - EasyPG for Emacs on OS X, or sometimes Emacs doesn’t load the env paths you might expect]] +• [[http://ubuntuforums.org/showthread.php?t=2155060][ubuntu forums]] Encrypting and decrypting a message + +------------------------------------------------------------------------------------------------------------------------- + +1 Made an edit here (initially, I only signed the file, without encrypting it). Thanks [[https://www.reddit.com/user/aminb][/u/aminab]] for [[https://www.reddit.com/r/emacs/comments/46fi6f/adding_mu4e_support_to_emacs_part_two_configuring/d04szm3][the + correction]]. [[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fnref-1][↩]] + +2 If you still have the .authinfo file, rename it. Once we see that mu4e sends emails using the encrypted + version of the auth file, we can dispose this, decrypted, version of it. [[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fnref-2][↩]] + +3 If Emacs asks for your passphrase too often, you might find this [[https://www.reddit.com/r/emacs/comments/45lx1s/adding_mu4e_support_to_emacs_the_hard_way/d01b1hu][comment in Reddit]], by [[https://www.reddit.com/user/aminb][/u/aminb]], + helpful. [[http://prodissues.com/2016/02/emacs-gpg-for-dummies.html#fnref-3][↩]] + +Powered by [[http://getpelican.com/][Pelican]]. Written in [[http://python.org][Python]]. Codebase on [[https://github.com/yanivdll/prodissues][Github]] +** [[https://xenodium.com/emacs-chaining-org-babel-blocks/][Emacs: chaining org babel blocks]] +Article Link: https://xenodium.com/emacs-chaining-org-babel-blocks/ +Captured On: [2024-04-13 Sat 04:11] +Álvaro Ramírez + +27 October 2020 Emacs: chaining org babel blocks + +Recently wanted to chain org babel blocks. That is, aggregate separate source blocks and execute as one +combined block. + +chain.gif + +I wanted the chaining primarily driven through header arguments as follows: + +#+name: block-0 +#+begin_src swift + print("hello 0") +#+end_src + +#+name: block-1 +#+begin_src swift :include block-0 + print("hello 1") +#+end_src + +#+RESULTS: block-1 +: hello 0 +: hello 1 + +I didn't find the above syntax and behaviour supported out of the box (or didn't search hard enough?). +Fortunately, this is our beloved and malleable editor, so we can always bend it our way! Wasn't quite sure +how to go about it, so I looked at other babel packages for inspiration. [[https://github.com/astahlman/ob-async][ob-async]] was great for that. + +Turns out, advicing org-babel-execute-src-block did the job: + +(defun adviced:org-babel-execute-src-block (&optional orig-fun arg info params) + (let ((body (nth 1 info)) + (include (assoc :include (nth 2 info))) + (named-blocks (org-element-map (org-element-parse-buffer) + 'src-block (lambda (item) + (when (org-element-property :name item) + (cons (org-element-property :name item) + item)))))) + (while include + (unless (cdr include) + (user-error ":include without value" (cdr include))) + (unless (assoc (cdr include) named-blocks) + (user-error "source block \"%s\" not found" (cdr include))) + (setq body (concat (org-element-property :value (cdr (assoc (cdr include) named-blocks))) + body)) + (setf (nth 1 info) body) + (setq include (assoc :include + (org-babel-parse-header-arguments + (org-element-property :parameters (cdr (assoc (cdr include) named-blocks))))))) + (funcall orig-fun arg info params))) + +(advice-add 'org-babel-execute-src-block :around 'adviced:org-babel-execute-src-block) + +Before I built my own support, I did find that [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][noweb]] got me most of what I needed, but required sprinkling +blocks with placeholder references. + +noweb.gif + +Combining [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][:noweb]] and [[https://org-babel.readthedocs.io/en/latest/header-args/#prologue][:prologue]] would have been a great match, if only prologue did expand the noweb +reference. I'm sure there's an alternative I'm missing. Either way, it was fun to poke at babel blocks and +build my own chaining support. +** [[https://xenodium.com/emacs-password-protect-current-pdf-revisited/][Emacs: Password-protect current pdf (revisited)]] +Article Link: https://xenodium.com/emacs-password-protect-current-pdf-revisited/ +Captured On: [2024-04-13 Sat 04:18] +Álvaro Ramírez + +09 July 2022 Emacs: Password-protect current pdf (revisited) + +UPDATE: [[https://github.com/xenodium/dwim-shell-command][dwim-shell-command]] is now available on [[https://melpa.org/#/dwim-shell-command][melpa]]. + +passprotect.gif + +With a recent look at writing [[https://xenodium.com/emacs-dwim-shell-command/][DWIM shell commands]], I've been revisiting my custom Emacs functions invoking +command line utilities. + +Take this [[https://xenodium.com/emacs-password-protect-current-pdf/][post]], for example, where I invoke [[https://github.com/qpdf/qpdf][qpdf]] via a elisp. Using the new +dwim-shell-command--on-marked-files in [[https://github.com/xenodium/dwim-shell-command/blob/main/dwim-shell-command.el][dwim-shell-command.el]], the code is stripped down to a bare minimum: + +(defun dwim-shell-commands-pdf-password-protect () + "Password protect pdf." + (interactive) + (dwim-shell-command-on-marked-files + "Password protect pdf" + (format "qpdf --verbose --encrypt '%s' '%s' 256 -- '<<f>>' '<<fne>>_enc.<<e>>'" + (read-passwd "user-password: ") + (read-passwd "owner-password: ")) + :utils "qpdf" + :extensions "pdf")) + +Compare the above dwim-shell-command--on-marked-files usage to my [[https://xenodium.com/emacs-password-protect-current-pdf/][previous implementation]]: + +(defun pdf-password-protect () + "Password protect current pdf in buffer or `dired' file." + (interactive) + (unless (executable-find "qpdf") + (user-error "qpdf not installed")) + (unless (equal "pdf" + (or (when (buffer-file-name) + (downcase (file-name-extension (buffer-file-name)))) + (when (dired-get-filename nil t) + (downcase (file-name-extension (dired-get-filename nil t)))))) + (user-error "no pdf to act on")) + (let* ((user-password (read-passwd "user-password: ")) + (owner-password (read-passwd "owner-password: ")) + (input (or (buffer-file-name) + (dired-get-filename nil t))) + (output (concat (file-name-sans-extension input) + "_enc.pdf"))) + (message + (string-trim + (shell-command-to-string + (format "qpdf --verbose --encrypt '%s' '%s' 256 -- '%s' '%s'" + user-password owner-password input output)))))) + +This really changes things for me. I'll be more inclined to add more of these tiny integrations to lots of +great command line utilities. Take this recent [[https://news.ycombinator.com/item?id=32028752][Hacker News post]] on [[https://github.com/ocrmypdf/OCRmyPDF][ocrmypdf]] as an example. Their [[https://ocrmypdf.readthedocs.io/en/latest/cookbook.html][cookbook]] +has lots of examples that can be easily used via dwim-shell-command--on-marked-files. + +What command line utils would you use? +** [[https://xenodium.com/emacs-dwim-shell-command-multi-language/][Emacs: DWIM shell command (multi-language)]] +Article Link: https://xenodium.com/emacs-dwim-shell-command-multi-language/ +Captured On: [2024-04-13 Sat 04:19] +Álvaro Ramírez + +10 July 2022 Emacs: DWIM shell command (multi-language) + +UPDATE: [[https://github.com/xenodium/dwim-shell-command][dwim-shell-command]] is now available on [[https://melpa.org/#/dwim-shell-command][melpa]]. + +csv.gif + +I keep on [[https://xenodium.com/png-to-icns-emacs-dwim-style/][goofying around]] with [[https://github.com/xenodium/dwim-shell-command][dwim-shell-command]] and it's sibling dwim-shell-command-on-marked-files from +[[https://github.com/xenodium/dwim-shell-command/blob/main/dwim-shell-command.el][dwim-shell-command.el]]. + +In addition to defaulting to [[https://en.wikipedia.org/wiki/Z_shell][zsh]], dwim-shell-command-on-marked-files now support other shells and languages. +This comes in handy if you have snippets in different languages and would like to easily invoke them from +Emacs. Multi-language support enables "using the best tool for the job" kinda thing. Or maybe you just +happen to know how to solve a particular problem in a specific language. + +Let's assume you have an existing Python snippet to convert files from csv to json. With +dwim-shell-command-on-marked-files, you can invoke the Python snippet to operate on either [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html][dired]] or buffer +files. + +(defun dwim-shell-command-csv-to-json-via-python () + "Convert csv file to json (via Python)." + (interactive) + (dwim-shell-command-on-marked-files + "Convert csv file to json (via Python)." + " +import csv +import json +text = json.dumps({ \"values\": list(csv.reader(open('<<f>>')))}) +fpath = '<<fne>>.json' +with open(fpath , 'w') as f: + f.write(text)" + :shell-util "python" + :shell-args "-c")) + +Or, maybe you prefer Swift and already had a snippet for the same thing? + +(defun dwim-shell-command-csv-to-json-via-swift () + "Convert csv file to json (via Swift)." + (interactive) + (dwim-shell-command-on-marked-files + "Convert csv file to json (via Swift)." + " + import Foundation + import TabularData + let filePath = \"<<f>>\" + print(\"reading \\(filePath)\") + let content = try String(contentsOfFile: filePath).trimmingCharacters(in: .whitespacesAndNewlines) + let parsedCSV = content.components(separatedBy: CSVWritingOptions().newline).map{ + $0.components(separatedBy: \",\") + } + let jsonEncoder = JSONEncoder() + let jsonData = try jsonEncoder.encode([\"value\": parsedCSV]) + let json = String(data: jsonData, encoding: String.Encoding.utf8) + let outURL = URL(fileURLWithPath:\"<<fne>>.json\") + try json!.write(to: outURL, atomically: true, encoding: String.Encoding.utf8) + print(\"wrote \\(outURL)\")" + :shell-pipe "swift -")) + +You can surely solve the same problem in elisp, but hey it's nice to have options and flexibility. diff --git a/assets/abbrev_defs b/assets/abbrev_defs index fa90cf85..aaeba01c 100644 --- a/assets/abbrev_defs +++ b/assets/abbrev_defs @@ -27,7 +27,7 @@ ("advizable" "advisable" nil :count 0) ("agression" "aggression" nil :count 0) ("agressive" "aggressive" nil :count 0) - ("ahve" "have" nil :count 0) + ("ahve" "have" nil :count 1) ("aknowledge" "acknowledge" nil :count 0) ("alegiance" "allegiance" nil :count 0) ("allegaince" "allegiance" nil :count 0) @@ -69,6 +69,7 @@ ("calender" "calendar" nil :count 0) ("camoflage" "camouflage" nil :count 0) ("camoflague" "camouflage" nil :count 0) + ("cancelled" "is" nil :count 0) ("carribean" "caribbean" nil :count 0) ("catagory" "category" nil :count 0) ("caugt" "caught" nil :count 0) @@ -80,6 +81,7 @@ ("chastized" "chastised" nil :count 0) ("cheif" "chief" nil :count 0) ("chrisitne" "christine" nil :count 0) + ("chrsitine's" "christine's" nil :count 0) ("collaegue" "colleague" nil :count 0) ("collegue" "colleague" nil :count 0) ("colum" "column" nil :count 0) @@ -96,12 +98,13 @@ ("congradulate" "congratulate" nil :count 0) ("consciencious" "conscientious" nil :count 0) ("consious" "conscious" nil :count 0) + ("continute" "continue" nil :count 0) ("contraversy" "controversy" nil :count 0) - ("cooly" "coolly" nil :count 0) ("creditscards" "credit cards" nil :count 0) ("customizaton" "customization" nil :count 0) ("dacquiri" "daiquiri" nil :count 0) - ("daneel" "danneel" nil :count 0) + ("daneel" "Danneel" nil :count 0) + ("danneel" "Danneel" nil :count 0) ("daquiri" "daiquiri" nil :count 0) ("decieve" "deceive" nil :count 0) ("decypher" "decipher" nil :count 0) @@ -122,6 +125,7 @@ ("drunkeness" "drunkenness" nil :count 0) ("dumbell" "dumbbell" nil :count 0) ("embarass" "embarrass" nil :count 0) + ("encompasing" "encompassing" nil :count 0) ("endoing" "weeks" nil :count 0) ("excede" "exceed" nil :count 0) ("exilerate" "exhilarate" nil :count 0) @@ -187,6 +191,7 @@ ("marshmellow" "marshmallow" nil :count 0) ("medeval" "medieval" nil :count 0) ("medevil" "medieval" nil :count 0) + ("mediatate" "meditate" nil :count 0) ("mentione" "mentioned" nil :count 0) ("mideval" "medieval" nil :count 0) ("milennium" "millennium" nil :count 0) @@ -199,10 +204,12 @@ ("misspel" "misspell" nil :count 0) ("momento" "memento" nil :count 0) ("momment" "the" nil :count 0) + ("mysoginistic" "misogynistic" nil :count 0) ("necessery" "necessary" nil :count 0) ("neice" "niece" nil :count 0) ("nieghbor" "neighbor" nil :count 0) ("noticable" "noticeable" nil :count 0) + ("numnber" "hour" nil :count 0) ("numnbers" "numbers" nil :count 0) ("occasionaly" "occasionally" nil :count 0) ("occured" "occurred" nil :count 0) @@ -212,7 +219,7 @@ ("omision" "omission" nil :count 0) ("ommision" "omission" nil :count 0) ("orignal" "original" nil :count 0) - ("ot" "to" nil :count 4) + ("ot" "to" nil :count 5) ("otehr" "other" nil :count 2) ("otes" "notes" nil :count 0) ("outgoign" "outgoing" nil :count 0) @@ -230,6 +237,7 @@ ("plagerize" "plagiarize" nil :count 0) ("platofrm" "platform" nil :count 0) ("poltical" "political" nil :count 0) + ("portugese" "portuguese" nil :count 0) ("posession" "possession" nil :count 0) ("possesion" "possession" nil :count 0) ("potatos" "potatoes" nil :count 0) @@ -250,7 +258,7 @@ ("rececently" "recently" nil :count 0) ("receivved" "received" nil :count 0) ("reciept" "receipt" nil :count 0) - ("recieve" "receive" nil :count 0) + ("recieve" "receive" nil :count 1) ("recomend" "recommend" nil :count 0) ("referance" "reference" nil :count 0) ("refered" "referred" nil :count 0) @@ -269,13 +277,14 @@ ("rythem" "rhythm" nil :count 0) ("rythm" "rhythm" nil :count 0) ("sargent" "sergeant" nil :count 0) + ("scheudle" "schedule" nil :count 0) ("secratary" "secretary" nil :count 0) ("secretery" "secretary" nil :count 0) ("seperate" "separate" nil :count 0) ("seperately" "separately" nil :count 0) ("sergent" "sergeant" nil :count 0) ("sgould" "should" nil :count 0) - ("shoudl" "should" nil :count 1) + ("shoudl" "should" nil :count 5) ("sieze" "seize" nil :count 0) ("skilfull" "skillful" nil :count 0) ("snoer" "snore" nil :count 1) @@ -293,9 +302,9 @@ ("takss" "tasks" nil :count 1) ("talekd" "talked" nil :count 0) ("talkign" "talking" nil :count 5) - ("teh" "the" nil :count 56) + ("teh" "the" nil :count 61) ("tehir" "their" nil :count 3) - ("tehre" "there" nil :count 1) + ("tehre" "there" nil :count 2) ("thansk" "thanks" nil :count 2) ("thickeness" "adjust" nil :count 0) ("throguh" "through" nil :count 0) @@ -317,7 +326,7 @@ ("valiedate" "and" nil :count 0) ("vehical" "vehicle" nil :count 0) ("visious" "vicious" nil :count 0) - ("waht" "what" nil :count 1) + ("waht" "what" nil :count 2) ("warant" "warrant" nil :count 0) ("welfair" "welfare" nil :count 0) ("welomce" "welcome" nil :count 0) diff --git a/custom/org-checklist.el b/custom/org-checklist.el new file mode 100644 index 00000000..e7d9b468 --- /dev/null +++ b/custom/org-checklist.el @@ -0,0 +1,153 @@ +;;; org-checklist.el --- org functions for checklist handling -*- lexical-binding: t; -*- + +;; Copyright (C) 2008-2014, 2021 James TD Smith + +;; Author: James TD Smith (@ ahktenzero (. mohorovi cc)) +;; Version: 1.0 +;; Keywords: org, checklists +;; +;; This file is not part of GNU Emacs. +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This file provides some functions for handing repeated tasks which involve +;; checking off a list of items. By setting the RESET_CHECK_BOXES property in an +;; item, when the TODO state is set to done all checkboxes under that item are +;; cleared. If the LIST_EXPORT_BASENAME property is set, a file will be created +;; using the value of that property plus a timestamp, containing all the items +;; in the list which are not checked. Additionally the user will be prompted to +;; print the list. +;; +;; I use this for to keep track of stores of various things (food stores, +;; components etc) which I check periodically and use the exported list of items +;; which are not present as a shopping list. +;; +;;; Usage: +;; (require 'org-checklist) +;; +;; Set the RESET_CHECK_BOXES and LIST_EXPORT_BASENAME properties in items as +;; needed. +;; +;; https://orgmode.org/worg/org-contrib/org-checklist.html +;; https://git.sr.ht/~bzg/org-contrib/blob/master/lisp/org-checklist.el +;; +;;; Code: +(require 'org) +(defvar org-state) +;; FIXME: This library requires +;; https://git.savannah.gnu.org/cgit/a2ps.git/tree/contrib/emacs/a2ps-print.el file +;; It is a part of a2ps distribution. +(load "a2ps-print" 'no-error) +(defvar a2ps-switches) +(declare-function a2ps-buffer "a2ps-print" (argp)) + +(setq org-default-properties (cons "RESET_CHECK_BOXES" (cons "LIST_EXPORT_BASENAME" org-default-properties))) + +(defgroup org-checklist nil + "Extended checklist handling for org" + :tag "Org-checklist" + :group 'org) + +(defcustom org-checklist-export-time-format "%Y%m%d%H%M" + "The format of timestamp appended to LIST_EXPORT_BASENAME to + make the name of the export file." + :link '(function-link format-time-string) + :group 'org-checklist + :type 'string) + +(defcustom org-checklist-export-function 'org-export-as-ascii + "function used to prepare the export file for printing" + :group 'org-checklist + :type '(radio (function-item :tag "ascii text" org-export-as-ascii) + (function-item :tag "HTML" org-export-as-html) + (function-item :tag "LaTeX" :value org-export-as-latex) + (function-item :tag "XOXO" :value org-export-as-xoxo))) + +(defcustom org-checklist-export-params nil + "options for the export function file for printing" + :group 'org-checklist + :type '(repeat string)) + +(defcustom org-checklist-a2ps-params nil + "options for a2ps for printing" + :group 'org-checklist + :type '(repeat string)) + +(defun org-reset-checkbox-state-maybe () + "Reset all checkboxes in an entry if the `RESET_CHECK_BOXES' property is set" + (interactive "*") + (if (org-entry-get (point) "RESET_CHECK_BOXES") + (org-reset-checkbox-state-subtree))) + + +(defun org-make-checklist-export () + "Produce a checklist containing all unchecked items from a list +of checkbox items" + (interactive "*") + (when (org-entry-get (point) "LIST_EXPORT_BASENAME") + (let* ((export-file (concat (org-entry-get (point) "LIST_EXPORT_BASENAME" nil) + "-" (format-time-string + org-checklist-export-time-format) + ".org")) + (print (pcase (org-entry-get (point) "PRINT_EXPORT" nil) + (`(or "" "nil" nil) nil) + (`nil (y-or-n-p "Print list? ")) + (_ t))) + exported-lines + (title "Checklist export")) + (save-restriction + (save-excursion + (org-narrow-to-subtree) + (org-update-checkbox-count-maybe) + (if (fboundp 'org-fold-show-subtree) + (org-fold-show-subtree) + (with-no-warnings (org-show-subtree))) + (goto-char (point-min)) + (when (looking-at org-complex-heading-regexp) + (setq title (match-string 4))) + (goto-char (point-min)) + (let ((end (point-max))) + (while (< (point) end) + (when (and (org-at-item-checkbox-p) + (or (string= (match-string 0) "[ ]") + (string= (match-string 0) "[-]"))) + (setq exported-lines + (nconc exported-lines (list (thing-at-point 'line))))) + (beginning-of-line 2))) + (set-buffer (get-buffer-create export-file)) + (org-insert-heading) + (insert (or title export-file) "\n") + (dolist (entry exported-lines) (insert entry)) + (org-update-checkbox-count-maybe) + (write-file export-file) + (when print + (funcall org-checklist-export-function + org-checklist-export-params) + (let* ((current-a2ps-switches a2ps-switches) + (a2ps-switches (append current-a2ps-switches + org-checklist-a2ps-params))) + (a2ps-buffer nil)))))))) + +(defun org-checklist () + (when (member org-state org-done-keywords) ;; org-state dynamically bound in org.el/org-todo + (org-make-checklist-export) + (org-reset-checkbox-state-maybe))) + +(add-hook 'org-after-todo-state-change-hook 'org-checklist) + +(provide 'org-checklist) + +;;; org-checklist.el ends here diff --git a/early-init.el b/early-init.el index ec15db20..2678815d 100644 --- a/early-init.el +++ b/early-init.el @@ -47,7 +47,12 @@ (setq debug-on-error nil) (setq debug-on-quit nil))) -;; ------------------------------ Compile Warnings ----------------------------- +;; -------------------------------- Compilation -------------------------------- + +;; Remove any .eln files inapplicable to this invocation +(setq native-compile-prune-cache t) + +;; --------------------------- Warning Notifications --------------------------- ;; log warnings, but don't popup the warnings buffer (setq native-comp-async-report-warnings-errors 'silent) @@ -56,7 +61,7 @@ (setq warning-minimum-level :error) ;; --------------------------- Use Online Repos Flag --------------------------- -;; set to nil to only use localrepo or the local elpa-mirrors +;; set to nil to only use localrepo and local elpa-mirrors (see script directory) (defvar cj/use-online-repos nil "Whether to check for network connectivity and use online package repositories.") @@ -106,7 +111,7 @@ "The user's home directory per the environment variable.") (defconst elpa-mirror-location (concat user-emacs-directory ".elpa-mirrors/") -"The path to the elpa mirror location.") + "The path to the elpa mirror location.") (setq package-archives nil) ;; package-archives will be added below @@ -139,17 +144,17 @@ ;; ONLINE REPOSITORIES (when (and (boundp 'cj/use-online-repos) cj/use-online-repos (internet-up-p)) - (progn - (add-to-list 'package-archives '("gnu". "https://elpa.gnu.org/packages/") t) - (add-to-list 'package-archive-priorities '("gnu" . 25)) - (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t) - (add-to-list 'package-archive-priorities '("nongnu" . 20)) - (add-to-list 'package-archives '("melpa". "https://melpa.org/packages/") t) - (add-to-list 'package-archive-priorities '("melpa" . 15)) - ;; (add-to-list 'package-archives '("org". "https://orgmode.org/packages/") t) - ;; (add-to-list 'package-archive-priorities '("org" . 10)) - (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) - (add-to-list 'package-archive-priorities '("melpa-stable" . 5)))) + (progn + (add-to-list 'package-archives '("gnu". "https://elpa.gnu.org/packages/") t) + (add-to-list 'package-archive-priorities '("gnu" . 25)) + (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t) + (add-to-list 'package-archive-priorities '("nongnu" . 20)) + (add-to-list 'package-archives '("melpa". "https://melpa.org/packages/") t) + (add-to-list 'package-archive-priorities '("melpa" . 15)) + ;; (add-to-list 'package-archives '("org". "https://orgmode.org/packages/") t) + ;; (add-to-list 'package-archive-priorities '("org" . 10)) + (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) + (add-to-list 'package-archive-priorities '("melpa-stable" . 5)))) (package-initialize) ;; only run refresh when there's no cache diff --git a/localrepo/archive-contents b/localrepo/archive-contents index 38364e6f..be5de3ec 100644 --- a/localrepo/archive-contents +++ b/localrepo/archive-contents @@ -45,7 +45,7 @@ (erc-hl-nicks . [(20200317 16) nil "ERC nick highlighter that ignores uniquifying chars when colorizing" tar]) (erc-image . [(20210604 753) nil "Show received image urls in the ERC buffer" tar]) (erc-yank . [(20210220 1815) nil "Automagically create a Gist if pasting more than 5 lines" tar]) - (eshell-toggle . [(20220718 729) ((emacs (25 1)) (dash (2 11 0))) "Show/hide eshell under active window." tar]) + (eshell-toggle . [(20240417 1536) ((emacs (25 1)) (dash (2 11 0))) "Show/hide eshell under active window." tar]) (esxml . [(20230308 2254) ((emacs (24 1)) (kv (0 0 5)) (cl-lib (0 5))) "Library for working with xml via esxml and sxml" tar]) (exec-path-from-shell . [(2 2) ((emacs (24 4))) "Get environment variables such as $PATH from the shell" tar]) (exercism . [(20231007 1253) ((emacs (27 1)) (dash (2 19 1)) (a (1 0 0)) (s (1 13 1)) (request (0 3 2)) (async (1 9 6)) (async-await (1 1)) (persist (0 5)) (transient (0 3 7))) "Unofficial https://exercism.org integration" tar]) @@ -87,8 +87,8 @@ (leetcode . [(20230524 1851) ((emacs (26 1)) (dash (2 16 0)) (graphql (0 1 1)) (spinner (1 7 3)) (aio (1 0)) (log4e (0 3 3))) "An leetcode client" tar]) (ligature . [(20220808 1225) ((emacs (28))) "Display typographical ligatures in major modes" tar]) (log4e . [(20240123 1313) nil "provide logging framework for elisp" tar]) - (lsp-mode . [(20240416 830) ((emacs (27 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0)) (eldoc (1 11))) "LSP mode" tar]) - (lsp-ui . [(20240406 2119) ((emacs (27 1)) (dash (2 18 0)) (lsp-mode (6 0)) (markdown-mode (2 3))) "UI modules for lsp-mode" tar]) + (lsp-mode . [(20240418 201) ((emacs (27 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0)) (eldoc (1 11))) "LSP mode" tar]) + (lsp-ui . [(20240416 2244) ((emacs (27 1)) (dash (2 18 0)) (lsp-mode (6 0)) (markdown-mode (2 3))) "UI modules for lsp-mode" tar]) (lv . [(0 15 0) nil "Other echo area" tar]) (madhat2r-theme . [(20170203 30) ((emacs (24))) "dark color theme that is easy on the eyes" tar]) (magit . [(3 3 0) ((emacs (25 1)) (dash (2 19 1)) (git-commit (3 3 0)) (magit-section (3 3 0)) (transient (0 3 6)) (with-editor (3 0 5))) "A Git porcelain inside Emacs" tar]) @@ -108,7 +108,6 @@ (org-contacts . [(1 1) ((emacs (27 1)) (org (9 3 4))) "Contacts management system for Org Mode" tar]) (org-drill . [(2 7 0) ((emacs (25 3)) (seq (2 14)) (org (9 3)) (persist (0 3))) "Self-testing using spaced repetition" tar]) (org-roam . [(20240114 1941) ((emacs (26 1)) (dash (2 13)) (org (9 4)) (emacsql (20230228)) (magit-section (3 0 0))) "A database abstraction layer for Org-mode" tar]) - (org-super-agenda . [(20240301 1602) ((emacs (26 1)) (compat (29 1 4 1)) (s (1 10 0)) (dash (2 13)) (org (9 0)) (ht (2 2)) (ts (0 2))) "Supercharge your agenda" tar]) (org-superstar . [(1 5 1) ((org (9 1 9)) (emacs (26 1))) "Prettify headings and plain lists in Org mode" tar]) (ox-hugo . [(20240305 1923) ((emacs (26 3)) (tomelr (0 4 3))) "Hugo Markdown Back-End for Org Export Engine" tar]) (package-build . [(20240415 1547) ((emacs (26 1)) (compat (27 1))) "Tools for assembling a package archive" tar]) @@ -144,7 +143,6 @@ (toml-mode . [(20161107 1800) ((emacs (24)) (cl-lib (0 5))) "Major mode for editing TOML files" tar]) (tree-sitter . [(20220212 1632) ((emacs (25 1)) (tsc (0 18 0))) "Incremental parsing system" tar]) (treesit-auto . [(20240401 1240) ((emacs (29 0))) "Automatically use tree-sitter enhanced major modes" tar]) - (ts . [(20220822 2313) ((emacs (26 1)) (dash (2 14 1)) (s (1 12 0))) "Timestamp and date/time library" tar]) (tsc . [(20220212 1632) ((emacs (25 1))) "Core Tree-sitter APIs" tar]) (visual-fill-column . [(2 6 3) ((emacs (25 1))) "fillmode" tar]) (vscode-icon . [(20230330 2206) ((emacs (25 1))) "Utility package to provide Vscode style icons" tar]) diff --git a/localrepo/eshell-toggle-20220718.729.tar b/localrepo/eshell-toggle-20240417.1536.tar Binary files differindex 7e8c5996..75c2885b 100644 --- a/localrepo/eshell-toggle-20220718.729.tar +++ b/localrepo/eshell-toggle-20240417.1536.tar diff --git a/localrepo/lsp-mode-20240416.830.tar b/localrepo/lsp-mode-20240418.201.tar Binary files differindex b3a15731..c538fb90 100644 --- a/localrepo/lsp-mode-20240416.830.tar +++ b/localrepo/lsp-mode-20240418.201.tar diff --git a/localrepo/lsp-ui-20240406.2119.tar b/localrepo/lsp-ui-20240416.2244.tar Binary files differindex 8491bf56..8dc8f3c6 100644 --- a/localrepo/lsp-ui-20240406.2119.tar +++ b/localrepo/lsp-ui-20240416.2244.tar diff --git a/localrepo/org-super-agenda-20240301.1602.tar b/localrepo/org-super-agenda-20240301.1602.tar Binary files differdeleted file mode 100644 index 885a7886..00000000 --- a/localrepo/org-super-agenda-20240301.1602.tar +++ /dev/null diff --git a/localrepo/ts-20220822.2313.tar b/localrepo/ts-20220822.2313.tar Binary files differdeleted file mode 100644 index 1c13f88f..00000000 --- a/localrepo/ts-20220822.2313.tar +++ /dev/null diff --git a/localrepo/vterm-20240325.1551.tar b/localrepo/vterm-20240325.1551.tar Binary files differindex 3212ef8a..43a90cb2 100644 --- a/localrepo/vterm-20240325.1551.tar +++ b/localrepo/vterm-20240325.1551.tar diff --git a/modules/custom-functions.el b/modules/custom-functions.el index 9f1875a5..a36dfe80 100644 --- a/modules/custom-functions.el +++ b/modules/custom-functions.el @@ -438,7 +438,7 @@ Uses `sortable-time-format' for the formatting the date/time." (interactive) (insert (format-time-string sortable-time-format (current-time)))) -(defvar sortable-date-format "%Y-%m-%d " +(defvar sortable-date-format "%Y-%m-%d %a" "Time format to insert with `insert-current-time' func. See help of `format-time-string' for possible replacements") @@ -556,6 +556,7 @@ Uses `sortable-time-format' for the formatting the date/time." (define-key map "-" #'cj/hyphenate-region) (define-key map "U" 'upcase-region) (define-key map "w" 'cj/remove-leading-trailing-whitespace) + (define-key map "W" 'fixup-whitespace) (define-key map "#" 'cj/count-words-buffer-or-region) (define-key map "1" 'cj/alphabetize-and-replace-region) (define-key map "C" 'display-fill-column-indicator-mode) diff --git a/modules/dirvish-config.el b/modules/dirvish-config.el index f35d2934..12f67519 100644 --- a/modules/dirvish-config.el +++ b/modules/dirvish-config.el @@ -31,6 +31,35 @@ ;; (add-hook 'dired-mode-hook 'auto-revert-mode) ;; auto revert dired when files change +;; -------------------------- Dired Copy Path As Kill -------------------------- +;; copies the full path of the file at point to the clipboard + +(defun cj/dired-copy-path-as-kill () + "Copy the full path of file at point in dired to the clipboard." + (interactive) + (let ((filename (dired-get-file-for-visit))) + (if (and filename (file-exists-p filename)) + (progn + (kill-new filename) + (message "Copied '%s' to clipboard." filename)) + (message "No file at point.")))) + +;; ------------------------ Dired Convert Image To Jpeg ------------------------ +;; converts the image at point to a jpeg + +(defun cj/dired-convert-image-to-jpeg () + (interactive) + (let* ((original-file (dired-get-file-for-visit)) + (file-extension (file-name-extension original-file)) + (jpeg-file (concat (file-name-sans-extension original-file) ".jpeg"))) + (if (member file-extension '("png" "bmp" "gif" "tif" "tiff" "svg" "webp")) + (if (string= file-extension "jpeg") + (message "File is already in JPEG format.") + (start-process "convert-to-jpeg" nil "convert" original-file jpeg-file) + (message "Conversion started for %s" original-file)) + (message (concat "File is not a supported image file type." + "Current supported types: " + "'png' 'bmp' 'gif' 'tif' 'tiff' 'svg' 'webp'"))))) ;; ------------------------ Dired Mark All Visible Files ----------------------- ;; convenience function to mark all visible files in dired @@ -55,7 +84,8 @@ '(("h" "~/" "home") ("rsb" "/sshx:cjennings@wolf.usbx.me:/home/cjennings/" "remote seedbox") ("rcj" "/sshx:cjennings@cjennings.net:~" "remote cjennings.net") - ("co" "~/code" "code") + ("co" "~/code" "code") + ("cj" "~/code/cjennings-net" "cjennings.net hugo") ("df" "~/.dotfiles/" "dotfiles") ("dn" "~/downloads/" "downloads") ("dr" "~/sync/org/drill/" "org drill files") @@ -95,10 +125,12 @@ (shell-command (concat "nitrogen --save --set-zoom-fill " (dired-file-name-at-point) " >>/dev/null 2>&1" )))) ("Z" . (lambda () (interactive) (cj/open-file-with-command "zathura"))) - ("p" . (lambda () (interactive) (cj/open-file-with-command "gimp"))) + ("P" . (lambda () (interactive) (cj/open-file-with-command "gimp"))) ("<left>" . dired-up-directory) ("<right>" . dired-find-file) ("f" . dirvish-file-info-menu) + ("p" . cj/dired-copy-path-as-kill) + ("C" . cj/dired-convert-image-to-jpeg) ("y" . dirvish-yank-menu) ("N" . dirvish-narrow) ("M" . cj/dired-mark-all-visible-files) diff --git a/modules/eshell-vterm-config.el b/modules/eshell-vterm-config.el index 7d37d9e5..c6e5cc1e 100644 --- a/modules/eshell-vterm-config.el +++ b/modules/eshell-vterm-config.el @@ -65,20 +65,26 @@ (add-to-list 'eshell-visual-options '("git" "--help" "--paginate")) ;; aliases - (eshell/alias "clear" "clear 1") ;; leaves the prompt at the top of the window - (eshell/alias "e" "find-file $*") + (eshell/alias "clear" "clear 1") ;; leaves prompt at the top of the window + (eshell/alias "e" "find-file $1") (eshell/alias "gocj" "cd /sshx:cjennings@cjennings.net:/var/cjennings/") (eshell/alias "gosb" "cd /sshx:cjennings@wolf.usbx.me:/home/cjennings/") (eshell/alias "gowolf" "cd /sshx:cjennings@wolf.usbx.me:/home/cjennings/") (eshell/alias "v" "eshell-exec-visual $*") - (eshell/alias "ff" "find-file-other-window $*") - (eshell/alias "f" "cj/eshell-find-using-dired $1") + (eshell/alias "ff" "find-file-other-window $1") + (eshell/alias "f" "find-using-dired $1") (eshell/alias "r" "ranger") - (eshell/alias "em" "find-file $*") - (eshell/alias "emacs" "find-file $*") + (eshell/alias "em" "find-file $1") + (eshell/alias "emacs" "find-file $1") (eshell/alias "ll" "ls -l")))) -(defun cj/eshell-find-using-dired (file-pattern) +(defun eshell/find-file-other-window (file) + (find-file-other-window (mapconcat 'identity file " "))) + +(defun eshell/find-file (file) + (find-file-other-window (mapconcat 'identity file " "))) + +(defun eshell/find-using-dired (file-pattern) "Find a file FILE-PATTERN' using 'find-name-dired'." (let ((escaped-pattern (regexp-quote file-pattern))) (find-name-dired . escaped-pattern))) diff --git a/modules/flyspell-config.el b/modules/flyspell-config.el index 6109eebc..ad0945c4 100644 --- a/modules/flyspell-config.el +++ b/modules/flyspell-config.el @@ -180,12 +180,5 @@ handled appropriately." (global-set-key (kbd "C-c f") 'flyspell-toggle ) -;; -------------------------------- Ispell STFU -------------------------------- -;; tell ispell where to send it's spurious error messages - -(advice-add 'ispell-lookup-words :around - (lambda (orig &rest args) - (shut-up (apply orig args)))) - (provide 'flyspell-config) ;;; flyspell-config.el ends here. diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el index 016fffcc..f15e5503 100644 --- a/modules/org-agenda-config.el +++ b/modules/org-agenda-config.el @@ -5,277 +5,216 @@ ;; Agenda views are tied to the F8 (fate) key. -;; f8 - DAILY SCHEDULE containing a events with a scheduled date or deadline of -;; the current day. This is followed by... -;; - TASK LIST containing tasks from all agenda sources. +;; f8 - MAIN AGENDA which organizes all tasks and events into: +;; - all unfinished priority A tasks +;; - the weekly schedule, including the habit consistency graph +;; - all priority B and C tasks -;; C-f8 - TASK LIST containing all tasks from all agenda sources +;; C-f8 - TASK LIST containing all tasks from all agenda targets. -;; M-f8 - TASK LIST containing all tasks from the current org-mode buffer. +;; M-f8 - TASK LIST containing all tasks from just the current org-mode buffer. ;; NOTE: ;; Files that contain information relevant to the agenda will be found in the ;; following places: the schedule-file, org-roam notes tagged as 'Projects' and ;; project todo.org files found in project-dir and code-dir. -;; How the agenda is created: -;; The inbox and schedule files are always included first. However, in order to -;; stay current, the files containing agenda information are queried before -;; calling the functions in the section org-agenda functions to display the -;; data. This way, any newly created events from project todo.org files, or -;; org-roam Project files will be included. - ;;; Code: -(with-eval-after-load 'org-roam - - ;; ----------------------------- Org TODO Settings --------------------------- - - (setq org-todo-keywords '((sequence "TODO(t)" "PROJECT(p)" "DOING(i)" - "WAITING(w)" "VERIFY(v)" "STALLED(s)" - "DELEGATED(x)" "|" - "FAILED(f)" "DONE(d)" "CANCELLED(c)"))) - - (setq org-todo-keyword-faces - '(("TODO" . "green") - ("PROJECT" . "blue") - ("DOING" . "yellow") - ("WAITING" . "white") - ("VERIFY" . "orange") - ("STALLED" . "light blue") - ("DELEGATED" . "green") - ("FAILED" . "red") - ("DONE" . "dark grey") - ("CANCELLED" . "dark grey"))) - - (setq org-highest-priority ?A) - (setq org-lowest-priority ?D) - (setq org-default-priority ?D) - (setq org-priority-faces '((?A . (:foreground "Cyan" :weight bold)) - (?B . (:foreground "Yellow")) - (?C . (:foreground "Green")) - (?D . (:foreground "Grey")))) - - (setq org-enforce-todo-dependencies t) - (setq org-enforce-todo-checkbox-dependencies t) - (setq org-deadline-warning-days 7) ;; warn me w/in a week of deadlines - (setq org-log-done nil) ;; don't log when tasks was done - - ;; inherit parents properties (no schedules or deadlines) - (setq org-use-property-inheritance t) - - ;; ------------------ Org TODO Next/Previous Set Keybindings ----------------- - - (add-hook 'org-agenda-mode-hook (lambda () - (local-set-key (kbd "s-<right>") #'org-agenda-todo-nextset) - (local-set-key (kbd "s-<left>") #'org-agenda-todo-previousset))) - - ;; ------------------------------ Org Super Agenda ----------------------------- - - (use-package org-super-agenda - :config (org-super-agenda-mode)) - -;;;; ORG AGENDA VIEW DEFINITIONS - (defun cj/agenda-today-view() - "This agenda from all tasks that are scheduled or have a deadline." - (setq org-super-agenda-groups - '((:log t) ; Automatically named "Log" - (:name "SCHEDULED AND DUE" - :time-grid t - :deadline past - :deadline today - :scheduled past - :scheduled today) - (:habit t) - (:name "DUE SOON" - :deadline future) - (:name "LESS IMPORTANT" - :scheduled future - :order 100) - (:discard (:anything t))))) - - (defun cj/agenda-all-view() - "This agenda is built from all tasks." - (setq org-super-agenda-groups - '( - (:name "Ready To Go" - :todo "STAGED" - :todo "READY" - :order 5) - (:name "Due Today" - :deadline past - :deadline today - :order 2) - (:name "Empty Projects" - :todo "PROJECT" - :order 85) - (:name "Delegated" - :todo "DELEGATED" - :order 50) - (:name "Scheduled Later" - :scheduled future - :order 75) - (:name "Scheduled Today" - :scheduled today - :scheduled past - :order 4) - (:name "In Progress" - :todo "DOING" - :order 7) - (:name "High Priority" - :priority "A" - :order 10) - (:name "Waiting" - :todo "WAITING" - :order 60) - (:name "Upcoming" - :deadline future - :order 30) - (:name "Next Priority" - :priority "B" - :order 70) - (:name "Everything Else" - :anything t - :order 90)))) - - ;; ------------------------------ Add Agenda Time ------------------------------ - - (defun cj/add-agenda-time (s) - "Add an event with time S to appear underneath the line-at-point. -This allows a line to show in an agenda without being scheduled or a deadline." - (interactive "sTime: ") - (defvar cj/timeformat "%Y-%m-%d %a") - (org-end-of-line) - (save-excursion - (open-line 1) - (forward-line 1) - (insert (concat "<" (format-time-string cj/timeformat (current-time)) " " s ">" )))) - - (global-set-key (kbd "M-t") #'cj/add-agenda-time) - - ;; ---------------------------- Org Agenda Settings ---------------------------- - +(use-package org-agenda + :ensure nil ;; built-in + :after (org org-roam) + :config (setq org-agenda-prefix-format '((agenda . " %i %-25:c%?-12t% s") (timeline . " % s") - (todo . " %i %-25:c") - (tags . " %i %-12:c") - (search . " %i %-12:c"))) + (todo . " %i %-25:c") + (tags . " %i %-12:c") + (search . " %i %-12:c"))) (setq org-agenda-dim-blocked-tasks 'invisible) (setq org-agenda-skip-scheduled-if-done nil) - (setq org-agenda-skip-include-deadlines t) (setq org-agenda-remove-tags t) (setq org-agenda-compact-blocks t) - ;; ------------------------ Add Files To Org Agenda List ----------------------- - ;; finds files named 'todo.org' (case insensitive) and adds them to - ;; org-agenda-files list. + ;; display the agenda from the bottom + (add-to-list 'display-buffer-alist + '("\\*Org Agenda\\*" + (display-buffer-reuse-mode-window display-buffer-below-selected) + (dedicated . t) + (window-height . fit-window-to-buffer))) - (defun cj/add-files-to-org-agenda-files (directory) - "Recursively searches for files named 'todo.org', - Searches in DIRECTORY and adds them to org-project-files." - (interactive "D") - (setq org-agenda-files - (append (directory-files-recursively directory - "^[Tt][Oo][Dd][Oo]\\.[Oo][Rr][Gg]$" t) - org-agenda-files))) + ;; reset s-left/right each time org-agenda is enabled + (add-hook 'org-agenda-mode-hook (lambda () + (local-set-key (kbd "s-<right>") #'org-agenda-todo-nextset) + (local-set-key (kbd "s-<left>") + #'org-agenda-todo-previousset))) + ;; build org-agenda-list for the first time after emacs init completes. + (add-hook 'emacs-startup-hook #'cj/build-org-agenda-list)) - ;; NOTE: the following functions require org-roam functionality - (with-eval-after-load 'org-roam-config - ;; ---------------------------- Rebuild Org Agenda --------------------------- +;; ------------------------ Add Files To Org Agenda List ----------------------- +;; finds files named 'todo.org' (case insensitive) and adds them to +;; org-agenda-files list. - (defun cj/build-org-agenda-list () - "Rebuilds the org agenda list. -Begins with the inbox-file and schedule-file, then searches for org-roam -Projects and adds all todo.org files from code and project directories." - (interactive) - ;; reset org-agenda-files to inbox-file - (setq org-agenda-files (list inbox-file schedule-file)) - (let ((new-files - (append - (cj/org-roam-list-notes-by-tag "Project")))) - (dolist (file new-files) - (unless (member file org-agenda-files) - (setq org-agenda-files (cons file org-agenda-files))))) +(defun cj/add-files-to-org-agenda-files-list (directory) + "Search for files named \\='todo.org\\=' add them to org-project-files. +DIRECTORY is a string of the path to begin the search." + (interactive "D") + (setq org-agenda-files + (append (directory-files-recursively directory + "^[Tt][Oo][Dd][Oo]\\.[Oo][Rr][Gg]$" t) + org-agenda-files))) - (cj/add-files-to-org-agenda-files projects-dir) - (cj/add-files-to-org-agenda-files code-dir)) +;; ---------------------------- Rebuild Org Agenda --------------------------- +;; builds the org agenda list from all agenda targets. +;; agenda targets is the schedule, project todos, inbox, and org roam projects. - ;; build org-agenda-list for the first time once emacs init is complete. - (add-hook 'emacs-startup-hook 'cj/build-org-agenda-list) - - ;; ------------------------ Org Agenda Display Functions ----------------------- - - (defun cj/agenda-all-agenda-files-day () - "Display an \'org-agenda\' schedule with tasks covering today. +(defun cj/build-org-agenda-list () + "Rebuilds the org agenda list. +Begins with the inbox-file and schedule-file, then searches for org-roam +Projects and adds all todo.org files from code and project directories." + (interactive) + ;; reset org-agenda-files to inbox-file + (setq org-agenda-files (list inbox-file schedule-file)) + (let ((new-files + (append + (cj/org-roam-list-notes-by-tag "Project")))) + (dolist (file new-files) + (unless (member file org-agenda-files) + (setq org-agenda-files (cons file org-agenda-files))))) + + (cj/add-files-to-org-agenda-files-list projects-dir) + (cj/add-files-to-org-agenda-files-list code-dir)) + +;; ------------------------------ Agenda List All ------------------------------ +;; an agenda listing tasks from all available agenda targets. + +(defun cj/todo-list-all-agenda-files () + "Displays an \\='org-agenda\\=' todo list. The contents of the agenda will be built from org-project-files and org-roam files that have project in their filetag." - (interactive) - (cj/build-org-agenda-list) - (setq org-agenda-span 'day) - (cj/agenda-today-view) - (org-agenda "a" "a")) - (global-set-key (kbd "<f8>") #'cj/agenda-all-agenda-files-day) + (interactive) + (cj/build-org-agenda-list) + (org-agenda "a" "t")) +(global-set-key (kbd "C-<f8>") #'cj/todo-list-all-agenda-files) + +;; ------------------------- Agenda List Current Buffer ------------------------ +;; an agenda listing tasks from just the current buffer. + +(defun cj/todo-list-from-this-buffer () + "Displays an \\='org-agenda\\=' todo list built from the current buffer. +If the current buffer isn't an org buffer, inform the user." + (interactive) + (if (eq major-mode 'org-mode) + (let ((org-agenda-files (list buffer-file-name))) + (org-agenda "a" "t")) + (message (concat "Your org agenda request based on '" (buffer-name (current-buffer)) + "' failed because it's not an org buffer.")))) +(global-set-key (kbd "M-<f8>") #'cj/todo-list-from-this-buffer) + +;; -------------------------------- Main Agenda -------------------------------- +;; my custom agenda command from all available agenda targets. adapted from: +;; https://blog.aaronbieber.com/2016/09/24/an-agenda-for-life-with-org-mode.html + +(defvar cj/main-agenda-hipri-title "\nHIGH PRIORITY UNRESOLVED TASKS\n" + "String to announce the high priority section of the main agenda.") + +(defvar cj/main-agenda-schedule-title "\nSCHEDULE\n" + "String to announce the schedule section of the main agenda.") + +(defvar cj/main-agenda-tasks-title "\nPRIORITY B AND C\n" + "String to announce the schedule section of the main agenda.") + +(defun cj/org-skip-subtree-if-habit () + "Skip an agenda entry if it has a STYLE property equal to \"habit\"." + (let ((subtree-end (save-excursion (org-end-of-subtree t)))) + (if (string= (org-entry-get nil "STYLE") "habit") + subtree-end + nil))) + +(defun cj/org-skip-subtree-if-priority (priority) + "Skip an agenda subtree if it has a priority of PRIORITY. +PRIORITY may be one of the characters ?A, ?B, or ?C." + (let ((subtree-end (save-excursion (org-end-of-subtree t))) + (pri-value (* 1000 (- org-lowest-priority priority))) + (pri-current (org-get-priority (thing-at-point 'line t)))) + (if (= pri-value pri-current) + subtree-end + nil))) + +(defun cj/org-skip-subtree-if-keyword (keywords) + "Skip an agenda subtree if it has a TODO keyword in KEYWORDS. +KEYWORDS must be a list of strings." + (let ((subtree-end (save-excursion (org-end-of-subtree t)))) + (if (member (org-get-todo-state) keywords) + subtree-end + nil))) + +(setq org-agenda-custom-commands + '(("d" "Daily Agenda with Tasks" + ((tags "PRIORITY=\"A\"" + ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) + (org-agenda-overriding-header cj/main-agenda-hipri-title))) + (agenda "" ((org-agenda-ndays 1) + (org-agenda-overriding-header cj/main-agenda-schedule-title))) + (alltodo "" + ((org-agenda-skip-function '(or (cj/org-skip-subtree-if-habit) + (cj/org-skip-subtree-if-priority ?A) + (cj/org-skip-subtree-if-priority ?D) + (cj/org-skip-subtree-if-keyword '("PROJECT")) + (org-agenda-skip-if nil '(scheduled deadline)))) + (org-agenda-overriding-header cj/main-agenda-tasks-title)))) + ((org-agenda-compact-blocks nil))))) + + + (defun cj/main-agenda-display () + "Display the main \'org-agenda\' display. +This uses all org-agenda targes and presents three sections: +- all unfinished priority A tasks +- the weekly schedule, including the habit consistency graph +- all priority B and C tasks" + (interactive) + (cj/build-org-agenda-list) + (org-agenda "a" "d")) +(global-set-key (kbd "<f8>") #'cj/main-agenda-display) +;; ------------------------- Add Timestamp To Org Entry ------------------------ +;; simply adds a timestamp to put the org entry on an agenda - (defun cj/agenda-all-agenda-files-week () - "Display an 'org-agenda' schedule with tasks covering this week. -The contents of the agenda will be built from org-project-files and org-roam -files that have project in their filetag." - (interactive) - (cj/build-org-agenda-list) - (setq org-agenda-span 'week) - (cj/agenda-today-view) - (org-agenda "a" "a")) - (global-set-key (kbd "s-<f8>") #'cj/agenda-all-agenda-files-week) - - (defun cj/todo-list-all-agenda-files () - "Displays an 'org-agenda' todo list. -The contents of the agenda will be built from org-project-files and org-roam -files that have project in their filetag." - (interactive) - (cj/build-org-agenda-list) - (cj/agenda-all-view) - (org-agenda "a" "t")) - (global-set-key (kbd "C-<f8>") #'cj/todo-list-all-agenda-files) - - (defun cj/todo-list-from-this-buffer () - "Displays an 'org-agenda' todo list built from the current buffer. - If the current buffer isn't an org buffer, inform the user." - (interactive) - (if (eq major-mode 'org-mode) - (let ((org-agenda-files (list buffer-file-name))) - (cj/agenda-all-view) - (org-agenda "a" "t")) - (message (concat "Your org agenda request based on '" (buffer-name (current-buffer)) - "' failed because it's not an org buffer.")))) - (global-set-key (kbd "M-<f8>") #'cj/todo-list-from-this-buffer) - - ;; --------------------------- Notifications / Alerts -------------------------- - ;; send libnotify notifications about agenda items - - (use-package alert - :defer .5 - :config - (setq alert-fade-time 10) ;; secs to vanish alert - (setq alert-default-style 'libnotify)) ;; work with dunst - - (use-package org-alert - :defer .5 - :bind - ("C-c A" . org-alert-check) - :config - (setq alert-default-style 'libnotify) ;; work with dunst - (setq org-alert-interval 300) ;; seconds checks agenda is checked (300 = 5 mins) - (setq org-alert-notify-cutoff 5) ;; minutes before a deadline to send alert - (setq org-alert-notify-after-event-cutoff 10) ;; mins post deadline to stop alerts - (setq org-alert-notification-title "Reminder") - (org-alert-enable)) - - - ) ;; end with-eval-after-load 'org-roam-config - ) ;; end with-eval-after-load 'org-roam +(defun cj/add-timestamp-to-org-entry (s) + "Add an event with time S to appear underneath the line-at-point. +This allows a line to show in an agenda without being scheduled or a deadline." + (interactive "sTime: ") + (defvar cj/timeformat "%Y-%m-%d %a") + (org-end-of-line) + (save-excursion + (open-line 1) + (forward-line 1) + (insert (concat "<" (format-time-string cj/timeformat (current-time)) " " s ">" )))) +(global-set-key (kbd "M-t") #'cj/add-timestamp-to-org-entry) + +;; --------------------------- Notifications / Alerts -------------------------- +;; send libnotify notifications about agenda items + +(use-package alert + :defer .5 + :after org-agenda + :config + (setq alert-fade-time 10) ;; secs to vanish alert + (setq alert-default-style 'libnotify)) ;; work with dunst + +(use-package org-alert + :defer .5 + :after alert + :bind + ("C-c A" . org-alert-check) + :config + (setq alert-default-style 'libnotify) ;; work with dunst + (setq org-alert-interval 180) ;; seconds between agenda checks (180 = 3 mins) + (setq org-alert-notify-cutoff 5) ;; minutes before a deadline to send alert + (setq org-alert-notify-after-event-cutoff 10) ;; mins post deadline to stop alerts + (setq org-alert-notification-title "Reminder") + (org-alert-enable)) (provide 'org-agenda-config) ;;; org-agenda-config.el ends here diff --git a/modules/org-capture-config.el b/modules/org-capture-config.el index 8feab2bf..4fa7af41 100644 --- a/modules/org-capture-config.el +++ b/modules/org-capture-config.el @@ -78,31 +78,57 @@ Intended to be called within an org capture template." ;; ORG-CAPTURE TEMPLATES (setq org-protocol-default-template-key "L") (setq org-capture-templates - '( + '(("t" "Task" entry (file+headline inbox-file "Inbox") + "* TODO %?" :prepend t) + ("e" "Event" entry (file+headline schedule-file "Scheduled Events") "* %?\nWHEN: %^t" :prepend t) ("E" "Epub Text" entry (file+headline inbox-file "Inbox") - "* %?\n#+BEGIN_QUOTE\n %i\n#+END_QUOTE\nSource: [[%:link][%(buffer-name (org-capture-get :original-buffer))]]\nCaptured On: %U" :prepend t) + "* %? +#+BEGIN_QUOTE\n %i\n#+END_QUOTE +Source: [[%:link][%(buffer-name (org-capture-get :original-buffer))]] +Captured On: %U" :prepend t) ("P" "PDF Text" entry (file+headline inbox-file "Inbox") - "* %?\n#+BEGIN_QUOTE\n %(org-capture-pdf-active-region)\n#+END_QUOTE\nSource:[[%L][%(buffer-name (org-capture-get :original-buffer))]]\nCaptured On: %U" :prepend t) + "* %? +#+BEGIN_QUOTE\n%(org-capture-pdf-active-region)\n#+END_QUOTE +Source:[[%L][%(buffer-name (org-capture-get :original-buffer))]] +Captured On: %U" :prepend t) ("p" "Link with Selection" entry (file+headline inbox-file "Inbox") - "* TODO %?\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n[[%:link][%:description]]\nCaptured On: %U" :prepend t) + "* %?%:description +#+BEGIN_QUOTE\n%i\n#+END_QUOTE +[[%:link][%:description]] +Captured On: %U\n" :prepend t) ("L" "Link" entry (file+headline inbox-file "Inbox") - "* TODO %?\n[[%:link][%:description]]\nCaptured On: %U" :prepend t) + "* %?%:description +[[%:link][%:description]]\nCaptured On: %U" :prepend t :immediate-finish t) ("m" "Mu4e Email" entry (file+headline inbox-file "Inbox") - "* TODO %?%(if (string= \"%i\" \"\") \"\" \"\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\")\n[[%:link][%:description]]\nCaptured On: %U" + "* TODO %? +%(if (string= \"%i\" \"\") \"\" \"\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\") +[[%:link][%:description]] +Captured On: %U" :prepend t) - ("w" "Web Page Clipper" plain - (function cj/org-webpage-clipper) + ("g" "Grocery List Item" item + (file+headline inbox-file "Grocery List") "%?") + + ("s" "Shopping List Entry" entry + (file+headline inbox-file "Shopping List") "* %?") + + ("w" "Web Page Clipper" plain + (function cj/org-webpage-clipper) "* %a\nArticle Link: %L\nCaptured On: %U\n\n" :immediate-finish t))) ) ;; end with-eval-after-load 'org +;; ---------------------------- Simple Task Capture ---------------------------- +;; the simplest way to capture a task. Also a simple way to write this function. + +(define-key global-map (kbd "C-S-t") (kbd "C-c c t")) + (provide 'org-capture-config) ;;; org-capture-config.el ends here. diff --git a/modules/org-config.el b/modules/org-config.el index 0094cc9d..f084a8c0 100644 --- a/modules/org-config.el +++ b/modules/org-config.el @@ -18,25 +18,88 @@ ;;; Code: -;;;; --------------------------- Constants --------------------------- +;; --------------------------------- Constants --------------------------------- ;; note: some constants used here are defined in init.el (defvar org-archive-location (concat sync-dir "/archives/archive.org::datetree/")) ;; location of archive file (defvar org-project-files (list schedule-file)) -;; ---------------------------- APT Sorting Function --------------------------- +;; ---------------------------- Org General Settings --------------------------- -(defun cj/org-reorder-list-apt () - "Sort the org header by three criteria: alpha, pri, then todo." - (interactive) - (save-excursion - (ignore-errors - (progn - (org-sort-entries t ?a) - (org-sort-entries t ?p) - (org-sort-entries t ?t) - (org-cycle) - (org-cycle))))) +(defun cj/org-general-settings () + "All general \='org-mode\=' settings are grouped and set in this function." + + ;; Unbind org-cycle-agenda-files keys for use elsewhere + (unbind-key "C-'" org-mode-map) + (unbind-key "C-," org-mode-map) + + ;; ORG-MODULES + ;; enable recognition of org-protocol:// as a parameter + ;; add org-habits + (require 'org-protocol) + (setq org-modules '(org-protocol ol-eww ol-w3m ol-info org-habit)) + + ;; GENERAL + (setq org-startup-folded t) ;; all org files should start in the folded state + (setq org-cycle-open-archived-trees t) ;; re-enable opening headings with archive tags with TAB + (setq org-outline-path-complete-in-steps nil) + (setq org-return-follows-link t) ;; hit return to follow an org-link + (setq org-list-allow-alphabetical t) ;; allow alpha ordered lists (i.e., a), A), a., etc.) + + ;; INDENTATION + (setq org-startup-indented t) ;; load org files indented + (setq org-adapt-indentation t) ;; adapt indentation to outline node level + (setq org-indent-indentation-per-level 2) ;; indent two character-widths per level + + ;; INLINE IMAGES + (setq org-startup-with-inline-images t) ;; preview images by default + (setq org-image-actual-width '(500)) ;; keep image sizes in check + + (setq org-bookmark-names-plist nil) ;; don't set org-capture bookmarks + + ;; force pdfs exported from org to open in emacs + (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))) + +;; ----------------------------- Org TODO Settings --------------------------- + +(defun cj/org-todo-settings () + "All org-todo related settings are grouped and set in this function." + + ;; logging task creation, task start, and task resolved states + (setq org-todo-keywords '((sequence "TODO(t!)" "PROJECT(p)" "DOING(i!)" + "WAITING(w)" "VERIFY(v)" "STALLED(s)" + "DELEGATED(x)" "|" + "FAILED(f!)" "DONE(d!)" "CANCELLED(c!)"))) + + (setq org-todo-keyword-faces + '(("TODO" . "green") + ("PROJECT" . "blue") + ("DOING" . "yellow") + ("WAITING" . "white") + ("VERIFY" . "orange") + ("STALLED" . "light blue") + ("DELEGATED" . "green") + ("FAILED" . "red") + ("DONE" . "dark grey") + ("CANCELLED" . "dark grey"))) + + (setq org-highest-priority ?A) + (setq org-lowest-priority ?D) + (setq org-default-priority ?D) + (setq org-priority-faces '((?A . (:foreground "Cyan" :weight bold)) + (?B . (:foreground "Yellow")) + (?C . (:foreground "Green")) + (?D . (:foreground "Grey")))) + + (setq org-enforce-todo-dependencies t) + (setq org-enforce-todo-checkbox-dependencies t) + (setq org-deadline-warning-days 7) ;; warn me w/in a week of deadlines + (setq org-treat-insert-todo-heading-as-state-change t) ;; log task creation + (setq org-log-into-drawer t) ;; log into the drawer + (setq org-habit-graph-column 75) ;; allow space for task name + + ;; inherit parents properties (sadly not schedules or deadlines) + (setq org-use-property-inheritance t)) ;; ---------------------------------- Org Mode --------------------------------- @@ -57,7 +120,6 @@ ("C-\\" . org-match-sparse-tree) ("C-c t" . org-set-tags-command) ("C-c l" . org-store-link) - ("C-c r" . cj/org-reorder-list-apt) ("C-c C-l" . org-insert-link) ("s-<up>" . org-priority-up) ("s-<down>" . org-priority-down) @@ -112,40 +174,38 @@ (org-mode . (lambda () (interactive) (company-mode -1))) ;; no company-mode in org :config - ;; Unbind org-cycle-agenda-files keys for use elsewhere - (unbind-key "C-'" org-mode-map) - (unbind-key "C-," org-mode-map) + (cj/org-general-settings) + (cj/org-todo-settings)) - ;; ORG-PROTOCOL - ;; enable recognition of org-protocol:// as a parameter - (require 'org-protocol) - (setq org-modules '(org-protocol ol-eww ol-w3m ol-info)) +;; ------------------------------- Org-Checklist ------------------------------- +;; needed for org-habits to reset checklists once task is complete +;; this was a part of org-contrib which was deprecated - ;; GENERAL - (setq org-startup-folded t) ;; all org files should start in the folded state - (setq org-cycle-open-archived-trees t) ;; re-enable opening headings with archive tags with TAB - (setq org-outline-path-complete-in-steps nil) - (setq org-return-follows-link t) ;; hit return to follow an org-link - (setq org-list-allow-alphabetical t) ;; allow alpha ordered lists (i.e., a), A), a., etc.) +(use-package org-checklist + :ensure nil ;; in custom folder + :after org + :load-path "custom/org-checklist.el") - ;; INDENTATION - (setq org-startup-indented t) ;; load org files indented - (setq org-adapt-indentation t) ;; adapt indentation to outline node level - (setq org-indent-indentation-per-level 2) ;; indent two character-widths per level +;; -------------------------- Org Link To Current File ------------------------- +;; get a link to the file the current buffer is associated with. - ;; INLINE IMAGES - (setq org-startup-with-inline-images t) ;; preview images by default - (setq org-image-actual-width '(500)) ;; keep image sizes in check - - (setq org-bookmark-names-plist nil) ;; don't set org-capture bookmarks - - ;; force pdfs exported from org to open in emacs - (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))) +(defun cj/org-link-to-current-buffer-file () + "Create an Org mode link to the current file and copy it to the clipboard. +The link is formatted as [[file:<file-path>][<file-name>]], +where <file-path> is the full path to the current file and <file-name> +is the name of the current file without any directory information. -;; https://www.reddit.com/r/orgmode/comments/n56fcv/important_the_contrib_directory_now_lives_outside/ -;; (use-package org-contrib -;; :after org) +If the current buffer is not associated with a file, the function will throw an +error." + (interactive) + (if (buffer-file-name) + (let* ((filename (buffer-file-name)) + (description (file-name-nondirectory filename)) + (link (format "[[file:%s][%s]]" filename description))) + (kill-new link) + (message "Copied Org link to current file to clipboard: %s" link)) + (user-error "Buffer isn't associated with a file, so no link sent to clipboard"))) (provide 'org-config) ;;; org-config.el ends here diff --git a/modules/org-roam-config.el b/modules/org-roam-config.el index c9a5bad1..d6c437b2 100644 --- a/modules/org-roam-config.el +++ b/modules/org-roam-config.el @@ -6,42 +6,48 @@ ;;; Code: - ;; ---------------------------------- Org Roam --------------------------------- (use-package org-roam :after org :defer .5 :custom - (org-roam-directory "~/sync/org/roam/") + (org-roam-directory roam-dir) (org-roam-dailies-directory "journal/") (org-roam-completion-everywhere t) (org-roam-dailies-capture-templates '(("d" "default" entry "* %<%I:%M:%S %p %Z> %?" - :if-new (file+head "%<%Y-%m-%d>.org" "#+FILETAGS: Journal\n#+TITLE: %<%Y-%m-%d>")))) + :if-new (file+head "%<%Y-%m-%d>.org" "#+FILETAGS: Journal +#+TITLE: %<%Y-%m-%d>")))) (org-roam-capture-templates - '(("d" "default" plain - "%?" + '(("d" "default" plain "%?" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n") :unnarrowed t) - ("v" "v2mom" plain (file "~/sync/org/roam/templates/v2mom.org") + ("v" "v2mom" plain + (function (lambda () (concat roam-dir "templates/v2mom.org"))) :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n") :unnarrowed t) - ("r" "recipe" plain (file "~/sync/org/roam/templates/recipe.org") - :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n#+CATEGORY: ${title}\n#+FILETAGS: Recipe") + ("r" "recipe" plain + (function (lambda () (concat roam-dir "templates/recipe.org"))) + :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title} + #+CATEGORY: ${title}\n#+FILETAGS: Recipe") :unnarrowed t) - ("p" "project" plain (file "~/sync/org/roam/templates/project.org") - :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n#+CATEGORY: ${title}\n#+FILETAGS: Project") + ("p" "project" plain + (function (lambda () (concat roam-dir "templates/project.org"))) + :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title} + #+CATEGORY: ${title}\n#+FILETAGS: Project") :unnarrowed t) - ("t" "topic" plain (file "~/sync/org/roam/templates/topic.org") - :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title}\n#+CATEGORY: ${title}\n#+FILETAGS: Topic") + ("t" "topic" plain + (function (lambda () (concat roam-dir "templates/topic.org"))) + :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+TITLE: ${title} + #+CATEGORY: ${title}\n#+FILETAGS: Topic") :unnarrowed t))) :bind (("C-c n ?" . org-roam-hydra/body) ("C-c n l" . org-roam-buffer-toggle) ("C-c n f" . org-roam-node-find) - ("C-c n p" . cj/org-roam-find-project) - ("C-c n r" . cj/org-roam-find-recipe) - ("C-c n t" . cj/org-roam-find-topic) + ("C-c n p" . cj/org-roam-find-node-project) + ("C-c n r" . cj/org-roam-find-node-recipe) + ("C-c n t" . cj/org-roam-find-node-topic) ("C-c n i" . org-roam-node-insert) :map org-mode-map ("C-M-i" . completion-at-point) @@ -59,9 +65,9 @@ ;; move closed tasks to today's journal when marked done (add-to-list 'org-after-todo-state-change-hook - (lambda () - (when (equal org-state "DONE") - (cj/org-roam-copy-todo-to-today)))) + (lambda () + (when (equal org-state "DONE") + (cj/org-roam-copy-todo-to-today)))) (require 'org-roam-dailies) ;; Ensures the keymap is available (org-roam-db-autosync-mode)) @@ -70,34 +76,40 @@ (defun cj/org-roam-node-insert-immediate (arg &rest args) "Create new node and insert its link immediately. -The prefix ARG . -ARGS represents the node name to link." +This is mainly a wrapper around org-roam-node-insert to achieve immediate finish +to the capture. The prefix ARG and ARGS are the filter function and the rest of +the arguments that org-roam-node-insert expects." (interactive "P") (let ((args (cons arg args)) - (org-roam-capture-templates (list (append (car org-roam-capture-templates) - '(:immediate-finish t))))) - (apply #'org-roam-node-insert args))) + (org-roam-capture-templates (list (append (car org-roam-capture-templates) + '(:immediate-finish t))))) + (apply #'org-roam-node-insert args))) (global-set-key (kbd "C-c n I") 'cj/org-roam-node-insert-immediate) ;; ------------------------- Tag Listing And Filtering ------------------------- (defun cj/org-roam-filter-by-tag (tag-name) (lambda (node) - (member tag-name (org-roam-node-tags node)))) + (member tag-name (org-roam-node-tags node)))) (defun cj/org-roam-list-notes-by-tag (tag-name) (mapcar #'org-roam-node-file - (seq-filter - (cj/org-roam-filter-by-tag tag-name) - (org-roam-node-list)))) + (seq-filter + (cj/org-roam-filter-by-tag tag-name) + (org-roam-node-list)))) ;; -------------------------- Org Roam Find Functions -------------------------- (defun cj/org-roam-find-node (tag template-key template-file) - "List all node of type \='TAG\=' in completing read for selection or creation." + "List all nodes of type \='TAG\=' in completing read for selection or creation. +Interactively find or create an Org-roam node with a given \='TAG\='. Newly +created nodes are added to the agenda and follow a template defined by +\='TEMPLATE-KEY\=' and \='TEMPLATE-FILE\='." + (interactive) ;; Add the project file to the agenda after capture is finished - (add-hook 'org-capture-after-finalize-hook #'cj/org-roam-add-node-to-agenda-files-finalize-hook) + (add-hook 'org-capture-after-finalize-hook + #'cj/org-roam-add-node-to-agenda-files-finalize-hook) ;; Select a project file to open, creating it if necessary (org-roam-node-find @@ -107,86 +119,86 @@ ARGS represents the node name to link." nil :templates `((,template-key ,tag plain (file ,template-file) - :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" ,(concat "#+TITLE: ${title}\n#+CATEGORY: ${title}\n#+FILETAGS: " tag)) - :unnarrowed t)))) + :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" + ,(concat "#+TITLE: ${title} +#+CATEGORY: ${title}\n#+FILETAGS: " tag)) + :unnarrowed t)))) -(defun cj/org-roam-find-topic () - "List all node of type \=`topic\=` in completing read for selection or creation." +(defun cj/org-roam-find-node-topic () + "List nodes of type \=`topic\=` in completing read for selection or creation." (interactive) - (cj/org-roam-find-node "Topic" "t" "~/sync/org/roam/templates/topic.org")) + (cj/org-roam-find-node "Topic" "t" (concat roam-dir "templates/topic.org"))) -(defun cj/org-roam-find-recipe () - "List all node of type \=`recipe\=` in completing read for selection or creation." +(defun cj/org-roam-find-node-recipe () + "List nodes of type \=`recipe\=` in completing read for selection or creation." (interactive) - (cj/org-roam-find-node "Recipe" "r" "~/sync/org/roam/templates/recipe.org")) + (cj/org-roam-find-node "Recipe" "r" (concat roam-dir "templates/recipe.org"))) + +(defun cj/org-roam-find-node-project () + "List nodes of type \='project\=' in completing read for selection or creation." -(defun cj/org-roam-find-project () - "List all node of type \='project\=' in completing read for selection or creation." (interactive) - (cj/org-roam-find-node "Project" "p" "~/sync/org/roam/templates/project.org")) + (cj/org-roam-find-node "Project" "p" (concat roam-dir "templates/project.org"))) ;; ---------------------- Org Capture After Finalize Hook ---------------------- (defun cj/org-roam-add-node-to-agenda-files-finalize-hook () "Add the captured project file to \='org-agenda-files\='." ;; Remove the hook since it was added temporarily - (remove-hook 'org-capture-after-finalize-hook #'cj/org-roam-add-node-to-agenda-files-finalize-hook) + (remove-hook 'org-capture-after-finalize-hook + #'cj/org-roam-add-node-to-agenda-files-finalize-hook) + ;; Add project file to the agenda list if the capture was confirmed (unless org-note-abort - (with-current-buffer (org-capture-get :buffer) - (add-to-list 'org-agenda-files (buffer-file-name))))) - -;; ------------------------------- Capture Inbox ------------------------------- - -(defun cj/org-roam-capture-inbox () - (interactive) - (org-roam-capture- :node (org-roam-node-create) - :templates '(("i" "inbox" plain "** %?" - :if-new (file+head+olp "~/sync/org/roam/inbox.org" - "#+TITLE: Inbox\n#+CATEGORY: Inbox\n#+FILETAGS: Project" - ("Inbox")))))) -(global-set-key (kbd "C-t") #'cj/org-roam-capture-inbox) + (with-current-buffer (org-capture-get :buffer) + (add-to-list 'org-agenda-files (buffer-file-name))))) ;; -------------------------------- Capture Task ------------------------------- -(defun cj/org-roam-capture-task () +(defun cj/org-roam-capture-task-into-project () + "Create a new project and add a task immediately to it." (interactive) ;; Add the project file to the agenda after capture is finished - (add-hook 'org-capture-after-finalize-hook #'cj/org-roam-add-node-to-agenda-files-finalize-hook) + (add-hook 'org-capture-after-finalize-hook + #'cj/org-roam-add-node-to-agenda-files-finalize-hook) ;; Capture the new task, creating the project file if necessary - (org-roam-capture- :node (org-roam-node-read - nil - (cj/org-roam-filter-by-tag "Project")) - :templates '(("p" "project" plain "** TODO %?" - :if-new (file+head+olp "%<%Y%m%d%H%M%S>-${slug}.org" - "#+TITLE: ${title}\n#+CATEGORY: ${title}\n#+FILETAGS: Project" - ("${title}")))))) -(global-set-key (kbd "C-c n t") #'cj/org-roam-capture-task) + (org-roam-capture- + :node (org-roam-node-read + nil + (cj/org-roam-filter-by-tag "Project")) + :templates '(("p" "project" plain "** TODO %?" + :if-new (file+head+olp "%<%Y%m%d%H%M%S>-${slug}.org" + "#+TITLE: ${title} +#+CATEGORY: ${title} +#+FILETAGS: Project" + ("${title}")))))) +(global-set-key (kbd "C-c n t") #'cj/org-roam-capture-task-into-project) ;; ------------------------ Org Roam Copy Done To Daily ------------------------ (defun cj/org-roam-copy-todo-to-today () + "Copy completed tasks to today's daily org-roam node." (interactive) (let ((org-refile-keep t) ;; Set this to nil to delete the original! - (org-roam-dailies-capture-templates - '(("t" "tasks" entry "%?" - :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+FILETAGS: Journal\n#+TITLE: %<%Y-%m-%d>\n" ("Completed Tasks"))))) - (org-after-refile-insert-hook #'save-buffer) - today-file - pos) - (save-window-excursion - (org-roam-dailies--capture (current-time) t) - (setq today-file (buffer-file-name)) - (setq pos (point))) - - ;; Only refile if the target file is different than the current file - (unless (equal (file-truename today-file) - (file-truename (buffer-file-name))) - (org-refile nil nil (list "Completed Tasks" today-file nil pos))))) - - + (org-roam-dailies-capture-templates + '(("t" "tasks" entry "%?" + :if-new (file+head+olp "%<%Y-%m-%d>.org" + "#+FILETAGS: Journal +#+TITLE: %<%Y-%m-%d>\n" ("Completed Tasks"))))) + (org-after-refile-insert-hook #'save-buffer) + today-file + pos) + (save-window-excursion + (org-roam-dailies--capture (current-time) t) + (setq today-file (buffer-file-name)) + (setq pos (point))) + + ;; Only refile if the target file is different than the current file + (unless (equal (file-truename today-file) + (file-truename (buffer-file-name))) + (org-refile nil nil (list "Completed Tasks" today-file nil pos))))) (provide 'org-roam-config) ;;; org-roam-config.el ends here. diff --git a/modules/prog-general.el b/modules/prog-general.el index b6b58494..2338564f 100644 --- a/modules/prog-general.el +++ b/modules/prog-general.el @@ -141,6 +141,28 @@ If none exists, it opens magit-status." :bind ("C-c s i" . ivy-yasnippet)) +;; ----------------------- Export Org To Markdown On Save ---------------------- +;; if the file has the proper header, saving an org file will also export to +;; markdown with a timestamp. The header is this: +;; # -*- org-auto-export-to-md: t; -*- +;; #+DATE: +;; and is available as the org-export-md snippet + +(defvar org-auto-export-to-md nil) + +(defun cj/export-org-to-md-on-save () + "Export the current org file to Markdown format on save." + (when (and (eq major-mode 'org-mode) + org-auto-export-to-md) + (save-excursion + (goto-char (point-min)) + (search-forward "#+DATE:") + (kill-line) + (insert (format-time-string " %Y-%m-%d %H:%M")) + (org-md-export-to-markdown)))) + +(add-hook 'after-save-hook 'cj/export-org-to-md-on-save) + ;; --------------------- Display Color On Color Declaration -------------------- ;; display the actual color as highlight to color hex code diff --git a/modules/prog-lisp.el b/modules/prog-lisp.el index 618108bd..59eb122b 100644 --- a/modules/prog-lisp.el +++ b/modules/prog-lisp.el @@ -31,15 +31,17 @@ ;; $ rlwrap sbcl ;;; Code: +(require 'ert) ;; -------------------------------- Elisp Setup -------------------------------- ;; run this on editing an elisp file (defun cj/elisp-setup () "My default code preferences for emacs-lisp." - (setq-default tab-width 4) ;; set the tab width to 4 spaces - (setq-default indent-tabs-mode -1) ;; disable tab characters - (setq-default fill-column 80)) + (setq-default tab-width 4) ;; set the tab width to 4 spaces + (setq-default indent-tabs-mode -1) ;; disable tab characters + (setq-default fill-column 80) ;; default column for gnu projects + (display-fill-column-indicator-mode)) ;; show where the 80th column is (add-hook 'emacs-lisp-mode-hook 'cj/elisp-setup) ;; ------------------------------ Emacs Lisp REPL ------------------------------ @@ -85,7 +87,7 @@ (defun cj/eval-and-run-all-tests-in-buffer () - "Delete loaded tests, evaluate current buffer. and run all loaded ERT tests." + "Delete any loaded tests, evaluate current buffer, and run loaded ERT tests." (interactive) (ert-delete-all-tests) (eval-buffer) diff --git a/modules/show-kill-ring.el b/modules/show-kill-ring.el index a1f3a637..dbe7c934 100644 --- a/modules/show-kill-ring.el +++ b/modules/show-kill-ring.el @@ -63,12 +63,11 @@ This makes it easy to figure out which prefix to pass to yank." ;; show it (goto-char (point-min)) (setq buffer-read-only t) - (set-buffer-modified-p nil) - - ;; it's better to leave the point in it's buffer - ;; so user can C-u (Item#) C-y in place. - ;; (pop-to-buffer buf) - )) + (set-buffer-modified-p nil) + ;; display-buffer rather than pop-to-buffer + ;; easier for user to C-u (item#) C-y + ;; while the point is where they want to yank + (display-buffer buf))) (defun show-kill-insert-item (item) "Insert an ITEM from the kill ring into the current buffer. diff --git a/modules/system-utils.el b/modules/system-utils.el index 0592f928..0ec287b9 100644 --- a/modules/system-utils.el +++ b/modules/system-utils.el @@ -25,7 +25,10 @@ ;; 'cj/buffer-bury-alive-list' via 'C-u C-x k' ;; BUFFER BURY ALIVE LIST -(defvar cj/buffer-bury-alive-list '("*dashboard*" "*scratch*" "*Messages*") +(defvar cj/buffer-bury-alive-list '("*dashboard*" + "*scratch*" + "*Messages*" + "*ChatGPT*") "Buffers that shouldn't be killed, but buried instead.") ;; KILL BUFFER AND WINDOW diff --git a/modules/test-code.el b/modules/test-code.el index 6c4fadcd..d9759e6e 100644 --- a/modules/test-code.el +++ b/modules/test-code.el @@ -34,13 +34,13 @@ (use-package easy-hugo :defer .5 :init - (setq easy-hugo-basedir "~/code/cjennings.net/") + (setq easy-hugo-basedir "~/code/cjennings-net/") (setq easy-hugo-url "https://cjennings.net") (setq easy-hugo-sshdomain "cjennings.net") (setq easy-hugo-root "/var/www/cjennings/") (setq easy-hugo-previewtime "300") (setq easy-hugo-postdir "content") - (setq easy-hugo-server-flags "-D") + (setq easy-hugo-server-flags "-D --noHTTPCache --disableFastRender") (setq easy-hugo-default-ext ".md") :bind ("C-c H" . easy-hugo) :config @@ -113,6 +113,7 @@ otherwise use the default location in `cj/recording-location'." (use-package wttrin :defer .5 :load-path ("~/code/emacs-wttrin") + :ensure nil ;; local package :preface ;; dependency for wttrin (use-package xterm-color diff --git a/modules/ui-theme.el b/modules/ui-theme.el index 64fa739b..8d83ac50 100644 --- a/modules/ui-theme.el +++ b/modules/ui-theme.el @@ -45,13 +45,15 @@ Unloads any other applied themes before applying the chosen theme." ;; persistence utility functions used by switch themes. (defvar theme-file (concat sync-dir "emacs-theme.persist") - "The location of the file to persist the theme name.") + "The location of the file to persist the theme name. +If you want your theme change to persist across instances, put this in a +directory that is sync'd across machines with this configuration.") -(defvar fallback-theme-name "wombat" +(defvar fallback-theme-name "modus-vivendi" "The name of the theme to fallback on. This is used then there's no file, or the theme name doesn't match -any of the installed themes. If theme name is 'nil', there will be -no theme.") +any of the installed themes. This should be a built-in theme. If theme name is +'nil', there will be no theme.") (defun cj/read-file-contents (filename) "Read FILENAME and return its content as a string. @@ -97,14 +99,14 @@ loading the file name, the fallback-theme-name is applied and saved." ;; if theme-name is nil, unload all themes and load fallback theme (if (or (string= theme-name "nil") (not theme-name)) (progn - (mapcar #'disable-theme custom-enabled-themes) - (cj/load-fallback-theme "Theme file not found or theme name in it is nil.")) + (mapcar #'disable-theme custom-enabled-themes) + (cj/load-fallback-theme "Theme file not found or theme name in it is nil.")) ;; apply theme name or if error, load fallback theme (condition-case err (load-theme (intern theme-name) t) (error - (cj/load-fallback-theme (concat "Error loading " theme-name - "."))))))) + (cj/load-fallback-theme (concat "Error loading " theme-name + "."))))))) (cj/load-theme-from-file) diff --git a/modules/vc-config.el b/modules/vc-config.el index a9b72507..769825a8 100644 --- a/modules/vc-config.el +++ b/modules/vc-config.el @@ -28,7 +28,14 @@ (setf vc-handled-backends nil) ;; magit is the only vc interface I use (setq magit-bury-buffer-function 'magit-restore-window-configuration) (setq git-commit-major-mode 'org-mode) ;; edit commit messages in org-mode - (setq magit-display-buffer-function 'magit-display-buffer-fullframe-status-topleft-v1)) + (setq magit-display-buffer-function + 'magit-display-buffer-fullframe-status-topleft-v1) + + ;; CLONING + (setq magit-clone-default-directory code-dir) ;; cloned repositories go here by default + (setq magit-clone-set-remote-head t) ;; do as git does for remote heads + (setq magit-clone-set-remote.pushDefault 'ask) ;; ask if origin is default + ) ;; end use-package magit ;; --------------------------------- Git Gutter -------------------------------- ;; mark changed lines since last commit in the margin diff --git a/tests/test-custom-org-agenda-functions.el b/tests/test-custom-org-agenda-functions.el new file mode 100644 index 00000000..44f9f43d --- /dev/null +++ b/tests/test-custom-org-agenda-functions.el @@ -0,0 +1,94 @@ +;;; test-custom-org-agenda-functions.el --- Tests for custom functions in org-agenda -*- lexical-binding: t; -*- + +;;; Commentary: +;; This tests the custom functions created to build the main agenda in org-agenda-config.el + +;;; Code: + +(add-to-list 'load-path (concat user-emacs-directory "modules")) +(require 'org-agenda-config) + +(ert-deftest test-cj/org-skip-subtree-if-habit-positive () + (with-temp-buffer + (insert "* TODO [#A] Test task\n") + (insert ":PROPERTIES:\n") + (insert ":STYLE: habit\n") + (insert ":RESET_CHECK_BOXES: t\n") + (insert ":END:\n") + (org-mode) + (goto-char (point-min)) + (should (not (eq nil (cj/org-skip-subtree-if-habit)))))) + +(ert-deftest test-cj/org-skip-subtree-if-habit-negative () + (with-temp-buffer + (insert "* TODO [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-habit))))) + +(ert-deftest test-cj/org-skip-subtree-if-priority-positive () + (with-temp-buffer + (insert "* TODO [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (not (eq nil (cj/org-skip-subtree-if-priority ?A)))))) + +(ert-deftest test-cj/org-skip-subtree-if-priority-negative () + (erase-buffer) + (insert "* TODO [#B] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-priority ?A)))) + +(ert-deftest test-cj/org-skip-subtree-if-priority-boundary0 () + (erase-buffer) + (insert "* TODO Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-priority ?A)))) + +(ert-deftest test-cj/org-skip-subtree-if-priority-boundary1 () + (erase-buffer) + (insert "* Test entry\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-priority ?A)))) + +(ert-deftest test-cj/org-skip-subtree-if-keyword-positive () + (with-temp-buffer + (insert "* TODO [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (not (eq nil (cj/org-skip-subtree-if-keyword '("TODO"))))))) + +(ert-deftest test-cj/org-skip-subtree-if-keyword-positive-multiple () + (with-temp-buffer + (insert "* PROJECT Test entry\n") + (org-mode) + (goto-char (point-min)) + (should (not (eq nil (cj/org-skip-subtree-if-keyword '("TODO" "PROJECT"))))))) + +(ert-deftest test-cj/org-skip-subtree-if-keyword-negative () + (erase-buffer) + (insert "* PROJECT [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-keyword '("TODO"))))) + +(ert-deftest test-cj/org-skip-subtree-if-keyword-negative-superset () + (erase-buffer) + (insert "* PROJECT [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-keyword '("TODOTODO"))))) + +(ert-deftest test-cj/org-skip-subtree-if-keyword-negative-multiple () + (erase-buffer) + (insert "* PROJECT [#A] Test task\n") + (org-mode) + (goto-char (point-min)) + (should (eq nil (cj/org-skip-subtree-if-keyword '("TODO" "DONE"))))) + + +(provide 'test-custom-org-agenda-functions) +;;; test-custom-org-agenda-functions.el ends here. |
