diff options
-rw-r--r-- | .dir-locals.el | 7 | ||||
-rw-r--r-- | emacs-init.org | 1460 |
2 files changed, 1163 insertions, 304 deletions
diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..05bfae2 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,7 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((org-mode . ((eval . (add-hook 'before-save-hook + (lambda nil + (fpi/tangle-async)) + nil t))))) diff --git a/emacs-init.org b/emacs-init.org index 77a5662..a91a2ea 100644 --- a/emacs-init.org +++ b/emacs-init.org @@ -1,4 +1,65 @@ #+PROPERTY: header-args:emacs-lisp :tangle tangle/emacs-init.el :results silent :noweb yes +* Contents :QUOTE:TOC_2_gh: +#+BEGIN_QUOTE +- [[#overview][Overview]] + - [[#about-this-document][About this document]] +- [[#base-settings][Base settings]] + - [[#setup-load-path][Setup load path]] + - [[#meta-packages][Meta packages]] + - [[#gui-interface][GUI Interface]] + - [[#font][Font]] + - [[#theme--faces][Theme & Faces]] + - [[#user-info][User info]] + - [[#desktop-module][Desktop module]] + - [[#customize][Customize]] + - [[#file-and-input-history][File and input history]] + - [[#local-variables][Local variables]] + - [[#personal-keymap][Personal keymap]] + - [[#base-commands-simpleel][Base commands (simple.el)]] +- [[#selection-and-search-methods][Selection and search methods]] + - [[#completion-frameworks][Completion frameworks]] + - [[#isearch-enhancements][isearch enhancements]] +- [[#directory-project-buffer-window-management][Directory, project, buffer, window management]] + - [[#dired][Dired]] + - [[#tramp][Tramp]] + - [[#git][Git]] + - [[#projectile][Projectile]] + - [[#working-with-buffers][Working with buffers]] + - [[#window-configuration][Window configuration]] + - [[#file-encryption][File encryption]] +- [[#applications-and-utilities][Applications and utilities]] + - [[#calendar][Calendar]] + - [[#pdfs][PDFs]] + - [[#latex][Latex]] + - [[#programming-languages][Programming languages]] + - [[#org-mode][Org mode]] + - [[#deft][Deft]] + - [[#shell][Shell]] + - [[#grep][Grep]] + - [[#proced][Proced]] + - [[#pass][Pass]] + - [[#ledger][Ledger]] + - [[#elfeed][Elfeed]] + - [[#plotting-data][Plotting data]] + - [[#html-renderer][HTML renderer]] + - [[#email][Email]] + - [[#footnote-mode][Footnote Mode]] + - [[#bbdb][BBDB]] + - [[#compile][Compile]] + - [[#context-aware-hydra][Context aware hydra]] +- [[#language-settings][Language settings]] + - [[#spellcheck][Spellcheck]] +- [[#interface][Interface]] + - [[#general][General]] + - [[#rainbow-mode][Rainbow mode]] + - [[#parentheses][Parentheses]] + - [[#whitespace][Whitespace]] + - [[#undo][Undo]] + - [[#electric-stuff][Electric stuff]] + - [[#writing-setup][Writing Setup]] +- [[#wrapping-up][Wrapping up]] +#+END_QUOTE + * Overview ** About this document This files contains all the elisp code normally placed in the .emacs @@ -30,10 +91,13 @@ disable ~buffer-auto-save-file-name~ for the files. (use-package org-crypt :config (org-crypt-use-before-save-magic) :custom - (org-tags-exclude-from-inheritance (quote ("crypt"))) (org-crypt-key "F1EF502F9E81D81381B1679AF973BBEA6994521B")) #+END_SRC +#+BEGIN_SRC emacs-lisp :noweb-ref org-custom-no-inheritance-tags :tangle no +"crypt" +#+END_SRC + I use =.org= configuration files also for my other dotfiles. To ensure they are tangled upon save I use this function. #+NAME: tangle-hook @@ -44,8 +108,23 @@ they are tangled upon save I use this function. (expand-file-name "git/projects/dotfiles/" (getenv "HOME"))) (org-babel-tangle) (message "%s tangled" buffer-file-name))) +(defmacro fpi/tangle-async (&optional file) + "Tangle FILE with a separate emacs instance. -(add-hook 'org-mode-hook (lambda () (add-hook 'before-save-hook #'fpi/tangle-dotfiles nil t)) t) +Note that this does not respect any customization of the tangle +process in your init file as it is not loaded. This uses the +emacs-async library." + (interactive) + (let ((file (or file (buffer-file-name)))) + (and file + (not (file-remote-p file)) + `(async-start + (lambda () + (require 'org) + (require 'org-clock) + (org-babel-tangle-file ,file) + (org-notify (format "Tangled %s" ,file)) + 'ignore))))) #+END_SRC As I use =org-crypt= all =.org= files need to be decrypted before tangling, saved without encrypting and encrypted after tangling and @@ -92,8 +171,7 @@ header argument in the source code. ;; package.el to enable use of list-packages <<package.el>> -;; (setq safe-local-variable-values (list (cons 'buffer-auto-save-file-name nil) - ;; (cons 'header-line-format " "))) + (setq vc-follow-symlinks t) ;; For use on Windows via SSH X-Forwarding @@ -237,9 +315,10 @@ only. #+end_src ** GUI Interface Disable most of the user interface. - #+BEGIN_SRC emacs-lisp (use-package emacs + :custom + <<emacs-custom>> :config (tooltip-mode -1) (tool-bar-mode -1) @@ -247,6 +326,12 @@ Disable most of the user interface. (scroll-bar-mode -1) ) #+END_SRC + +Audible bell is useless when the sound is turned off and annoying when sound is on. Instead use visible bell. +#+begin_src emacs-lisp :tangle no :noweb-ref emacs-custom +(visible-bell t) +#+end_src + In /awesomewm/ and other tiling window managers the emacs window leaves a gap at the bottom. This removes it. #+BEGIN_SRC emacs-lisp @@ -299,6 +384,218 @@ Instead of the above code I set the font directly using (set-face-attribute 'default nil :font "Hack-11") #+end_src +#+begin_src emacs-lisp +(use-package emacs + :commands (prot/font-set-face-attribute + prot/font-set-fonts + prot/font-set-font-size-family + prot/font-fonts-dwim) + :config + (setq x-underline-at-descent-line t) + (setq underline-minimum-offset 1) + + (defconst prot/font-fontconfig-params + "embeddedbitmap=false:autohint=false:hintstyle=hintslight" + "Additional parameters for the given font family. +These are specific to the fontconfig backend for GNU/Linux systems.") + + (defvar prot/font-set-fonts-hook nil + "Hook that is called after setting fonts. +See, for example, `prot/font-set-fonts'.") + + ;; The idea with this association list is to use font combinations + ;; that are suitable to the given point size and intended function. + ;; Basically, I have three modes: my laptop's small screen, my laptop + ;; attached to a larger external monitor in a desktop setup (my normal + ;; case), and when I do presentations (i.e. my videos on Emacs). + ;; + ;; I find that at smaller sizes the open and wide proportions of + ;; Hack+FiraGO combined with their more intense typographic colour + ;; work best, while the more compact Iosevka+Source Sans Pro are + ;; better at larger point sizes. The "desktop" combo is ideal for use + ;; on a larger monitor at a regular point size. The latter is what I + ;; typically use to write prose or code. + ;; + ;; Note that the "Hack" typeface mentioned here is my patched version + ;; of it, which uses some alternative glyphs, is built on top of the + ;; latest dev branch, and is meant to improve both the Roman and + ;; Italic variants (alt glyphs are part of the Hack project): + ;; https://gitlab.com/protesilaos/hackfontmod + (defconst prot/font-sizes-families-alist + '(("laptop" . (10.5 "Hack" "Source Sans Pro" 1)) + ("desktop" . (13 "Hack" "Alegreya" 4)) + ("presentation" . (19 "Iosevka SS08" "Source Sans Pro" 1))) + "Alist of desired point sizes and their typefaces. +Each association consists of a display type mapped to a point +size, followed by monospaced and proportionately-spaced font +names, and a difference in desired size between the latter two to +account for their innate differences in proportions (this number +represents pixels and is found empirically). + +The monospaced typeface is meant to be applied to the `default' +and `fixed-pitch' faces. The proportionately-space font is +intended for the `variable-pitch' face.") + + (defun prot/font-set-face-attribute (face family size &optional params) + "Set FACE font to FAMILY at SIZE with optional PARAMS." + (let ((params (if params + params + prot/font-fontconfig-params))) + (set-face-attribute + `,face nil :font + (format "%s-%s:%s" family (number-to-string size) params)))) + + + + + + (defun prot/font-set-fonts (&optional points font-mono font-var) + "Set default font size using presets. + +POINTS is the font's point size, represented as either '10' or +'10.5'. FONT-MONO should be a monospaced typeface, due to the +alignment requirements of the `fixed-pitch' face. FONT-VAR could +be a proportionately-spaced typeface or even a monospaced one, +since the `variable-pitch' it applies to is not supposed to be +spacing-sensitive. Both families must be represented as a string +holding the family's name." + (interactive) + (let* ((data prot/font-sizes-families-alist) + (displays (mapcar #'car data)) + (choice (if points + points + (completing-read "Pick display size: " displays nil t))) + (size (if points + points + (nth 1 (assoc `,choice data)))) + (mono (if font-mono + font-mono + (if (member choice displays) + (nth 2 (assoc `,choice data)) + nil))) + (var (if font-var + font-var + (if (member choice displays) + (nth 3 (assoc `,choice data)) + nil))) + (adjust (nth 4 (assoc `,choice data)))) + (when window-system + (dolist (face '(default fixed-pitch)) + (prot/font-set-face-attribute `,face mono size)) + (prot/font-set-face-attribute 'variable-pitch var (+ size adjust)))) + (run-hooks 'prot/font-switch-fonts-hook)) + + (defvar prot/font-monospaced-fonts-list + '("Hack" "Iosevka SS08" "Iosevka Slab" "Source Code Pro" + "Ubuntu Mono" "Fantasque Sans Mono" "DejaVu Sans Mono" + "Fira Code" "Victor Mono" "Roboto Mono") + "List of typefaces for coding. +See `prot/font-set-font-size-family' for how this is used.") + + (defun prot/font-set-font-size-family () + "Set point size and main typeface. +This command is intended for testing various font families at +some common point sizes. + +See `prot/font-set-fonts' for the function I would normally use +or `prot/font-fonts-dwim' which just wraps this one with that." + (interactive) + (let* ((fonts prot/font-monospaced-fonts-list) + (font (completing-read "Select main font: " fonts nil t)) + (nums (list 13 14 15 16)) + (sizes (mapcar 'number-to-string nums)) + (size (completing-read "Select or insert number: " sizes nil)) + (var (face-attribute 'variable-pitch :family))) + (dolist (face '(default fixed-pitch)) + (prot/font-set-face-attribute face font (string-to-number size))) + (prot/font-set-face-attribute 'variable-pitch var (string-to-number size)) + (run-hooks 'prot/font-switch-fonts-hook))) + + (defun prot/font-fonts-dwim (&optional arg) + "Set fonts interactively. +This is just a wrapper around `prot/font-set-fonts' and +`prot/font-set-font-size-family', whose sole purpose is to +economise on dedicated key bindings." + (interactive "P") + (if arg + (prot/font-set-font-size-family) + (prot/font-set-fonts))) + + (defvar prot/font-fonts-line-spacing-alist + '(("Iosevka SS08" . 1) + ("Iosevka Slab" . 1) + ("Source Code Pro" . 1) + ("Ubuntu Mono" . 2)) + "Font families in need of extra `line-spacing'. +See `prot/font-line-spacing' for how this is used.") + + (defvar prot/font-fonts-bold-weight-alist + '(("Source Code Pro" . semibold)) + "Font families in need of a variegated weight for `bold'. +See `prot/font-bold-face' for how this is used.") + + (defmacro prot/font-adjustment (fn doc alist cond1 cond2) + "Macro for functions that employ `prot/font-switch-fonts-hook'. +NAME is the name of the resulting function. DOC is its +docstring. ALIST is an assosiation list of cons cells. COND1 +and COND2 is the body of an `if' statement's 'if' and 'then' part +respectively." + `(defun ,fn () + ,doc + (let* ((data ,alist) + (fonts (mapcar #'car data)) + ;; REVIEW This should be adjusted to account for the + ;; possibility of a distinct font family for the `bold' + ;; face. + (font (face-attribute 'default :family)) + (x (cdr (assoc font data)))) + (if (member font fonts) + ,cond1 + ,cond2)))) + + (prot/font-adjustment + prot/font-line-spacing + "Determine desirable `line-spacing', based on font family." + prot/font-fonts-line-spacing-alist + (setq-default line-spacing `,x) + (setq-default line-spacing nil)) + + ;; XXX This will not work with every theme, but only those that + ;; inherit the `bold' face instead of specifying a weight property. + ;; The intent is to configure this once and have it propagate wherever + ;; a heavier weight is displayed. My Modus themes handle this + ;; properly. + (prot/font-adjustment + prot/font-bold-face + "Determine weight for the `bold' face, based on font family." + prot/font-fonts-bold-weight-alist + (set-face-attribute 'bold nil :weight `,x) + (set-face-attribute 'bold nil :weight 'bold)) + + (defun prot/font-fonts-per-monitor () + "Use font settings based on screen size. +Meant to be used at some early initialisation stage, such as with +`after-init-hook'." + (let* ((display (if (<= (display-pixel-width) 1366) + "laptop" + "desktop")) + (data prot/font-sizes-families-alist) + (size (cadr (assoc `,display data))) + (mono (nth 2 (assoc `,display data))) + (var (nth 3 (assoc `,display data))) + (adjust (nth 4 (assoc `,display data)))) + (dolist (face '(default fixed-pitch)) + (prot/font-set-face-attribute face mono size)) + (prot/font-set-face-attribute 'variable-pitch var (+ size adjust)) + (run-hooks 'prot/font-switch-fonts-hook))) + + :hook ((after-init-hook . prot/font-fonts-per-monitor) + (prot/font-set-fonts-hook . prot/font-line-spacing) + (prot/font-set-fonts-hook . prot/font-bold-face)) + ;; Awkward key because I do not need it very often. Maybe once a day. + ;; The "C-c f" is used elsewhere. + :bind ("C-c F" . prot/font-fonts-dwim)) +#+end_src ** Theme & Faces =hc-zenburn= is the theme I chose for a long time. Lately I started to appreciate light themes more. [[https://gitlab.com/protesilaos/modus-themes][modus-operandi]] is an interesting light @@ -316,8 +613,9 @@ defined, for example ~pdf-view-midnight-colors~. (defcustom fpi/dark-theme-list '(spacemacs-dark spacemacs-dark-customizations) "List of themes to activate when using a dark theme.") (defcustom fpi/current-theme 'light - "Currently activated theme variation." - :set #'fpi/set-and-reload-theme) + "Currently activated theme variation.") + +(fpi/load-themes) #+end_src Functions to load themes based on the ~fpi/current-theme~ setting and to toggle the current theme between light and dark. @@ -333,20 +631,15 @@ of `(format \"fpi/%s-theme-list\" fpi/current-theme)'" (let* ((theme-variation (or theme-variation fpi/current-theme)) (themes (eval (intern (format "fpi/%s-theme-list" theme-variation))))) (mapc (lambda (theme) (load-theme theme t)) themes))) -(defun fpi/set-and-reload-theme (symbol value) - "Set SYMBOL to VALUE and update themes. - -Set SYMBOL to VALUE with `set-default'if it is not already set to -that value and update the loaded themes afterwards." - (when (not (and (boundp symbol) (eq (eval symbol) value))) - (set-default symbol value) - (fpi/load-themes))) (defun fpi/toggle-theme () "Toggle between light and dark theme." (interactive) (if (eq fpi/current-theme 'light) - (customize-save-variable 'fpi/current-theme 'dark) - (customize-save-variable 'fpi/current-theme 'light))) + (progn + (customize-save-variable 'fpi/current-theme 'dark) + (fpi/load-themes)) + (customize-save-variable 'fpi/current-theme 'light) + (fpi/load-themes))) #+end_src #+begin_src emacs-lisp :tangle no :noweb-ref fpi-bindings (define-key fpi/toggle-map "dt" #'fpi/toggle-theme) @@ -744,12 +1037,6 @@ This call now creates a custom theme based on the settings in the sections (progn (deftheme spacemacs-light-customizations "My customizations to spacemacs-light (Created 2020-06-27)") (custom-theme-set-faces 'spacemacs-light-customizations - '(default - ((t - (:family "Hack" :background "#fbf8ef" :foreground "#1c1e1f")))) - '(variable-pitch - ((t - (:family "EtBookOt" :background nil :foreground "#1c1e1f" :height 1.2)))) '(header-line ((t (:background nil :inherit nil)))) @@ -791,7 +1078,7 @@ This call now creates a custom theme based on the settings in the sections ((t nil))) '(org-document-title ((t - (:inherit nil :family "EtBookOt" :height 1.8 :foreground "#1c1e1f" :underline nil)))) + (:inherit nil :height 1.8 :foreground "#1c1e1f" :underline nil)))) '(org-document-info ((t (:height 1.2 :slant italic)))) @@ -800,16 +1087,16 @@ This call now creates a custom theme based on the settings in the sections (:inherit shadow :height 0.6)))) '(org-level-1 ((t - (:inherit nil :family "EtBookOt" :height 1.6 :weight normal :slant normal :foreground "#1c1e1f")))) + (:height 1.6 :weight normal :slant normal :foreground "#1c1e1f")))) '(org-level-2 ((t - (:inherit nil :family "EtBookOt" :weight normal :height 1.3 :slant italic :foreground "#1c1e1f")))) + (:weight normal :height 1.3 :slant italic :foreground "#1c1e1f")))) '(org-level-3 ((t - (:inherit nil :family "EtBookOt" :weight normal :slant italic :height 1.2 :foreground "#1c1e1f")))) + (:weight normal :slant italic :height 1.2 :foreground "#1c1e1f")))) '(org-level-4 ((t - (:inherit nil :family "EtBookOt" :weight normal :slant italic :height 1.1 :foreground "#1c1e1f")))) + (:weight normal :slant italic :height 1.1 :foreground "#1c1e1f")))) '(org-level-5 ((t nil))) '(org-level-6 @@ -818,9 +1105,6 @@ This call now creates a custom theme based on the settings in the sections ((t nil))) '(org-level-8 ((t nil))) - '(org-headline-done - ((t - (:family "EtBookOt")))) '(org-quote ((t nil))) '(org-block @@ -890,7 +1174,7 @@ This call now creates a custom theme based on the settings in the sections (:foreground "#1c661c")))) '(org-scheduled-previously ((t - (:foreground "#002000")))) + (:foreground "#002900")))) '(org-agenda-done ((t (:foreground "#727280")))) @@ -902,10 +1186,10 @@ This call now creates a custom theme based on the settings in the sections (:foreground "#727280")))) '(org-table ((t - (:family "cmu typewriter text" :height 0.9 :background "#fbf8ef")))) + (:inherit fixed-pitch :height 0.9 :background "#fbf8ef")))) '(org-code ((t - (:inherit nil :family "cmu typewriter text" :foreground "#525254" :height 0.9)))) + (:inherit fixed-pitch :foreground "#525254" :height 0.9)))) '(font-latex-sectioning-0-face ((t nil))) '(font-latex-sectioning-1-face @@ -1040,7 +1324,7 @@ ln -siv $(pwd)/tangle/spacemacs-light-customizations-theme.el ~/.emacs.d/ (dark-cyan "#008b8b") (light-green "#4f774f") ;;#3f773f (dark-green "#1c661c") - (dark-green2 "#002000") + (dark-green2 "#002900") (region-dark "#2d2e2e") (region "#39393d") (slate "#8FA1B3") @@ -1067,10 +1351,11 @@ ln -siv $(pwd)/tangle/spacemacs-light-customizations-theme.el ~/.emacs.d/ :END: #+begin_src emacs-lisp :noweb-ref faces-spacemacs-light :tangle no ;; light -'('(default ((t (:family ,sans-mono-font :background ,bg-white :foreground ,bg-dark - ;; :height 75 - )))) - '(variable-pitch ((t (:family ,et-font :background nil :foreground ,bg-dark :height 1.2)))) +'( + ;; '(default ((t (:family ,sans-mono-font :background ,bg-white :foreground ,bg-dark + ;; ;; :height 75 + ;; )))) + ;; '(variable-pitch ((t (:family ,et-font :background nil :foreground ,bg-dark :height 1.2)))) '(header-line ((t (:background nil :inherit nil)))) '(show-paren-match ((t nil))) '(magit-section-heading ((t nil))) @@ -1086,18 +1371,18 @@ ln -siv $(pwd)/tangle/spacemacs-light-customizations-theme.el ~/.emacs.d/ '(powerline-inactive2 ((t (:background ,bg-white)))) '(highlight ((t (:background ,shade-white)))) '(hl-line ((t nil))) - '(org-document-title ((t (:inherit nil :family ,et-font :height 1.8 :foreground ,bg-dark :underline nil)))) + '(org-document-title ((t (:inherit nil :height 1.8 :foreground ,bg-dark :underline nil)))) '(org-document-info ((t (:height 1.2 :slant italic)))) '(org-archived ((t (:inherit shadow :height 0.6)))) - '(org-level-1 ((t (:inherit nil :family ,et-font :height 1.6 :weight normal :slant normal :foreground ,bg-dark)))) - '(org-level-2 ((t (:inherit nil :family ,et-font :weight normal :height 1.3 :slant italic :foreground ,bg-dark)))) - '(org-level-3 ((t (:inherit nil :family ,et-font :weight normal :slant italic :height 1.2 :foreground ,bg-dark)))) - '(org-level-4 ((t (:inherit nil :family ,et-font :weight normal :slant italic :height 1.1 :foreground ,bg-dark)))) + '(org-level-1 ((t (:height 1.6 :weight normal :slant normal :foreground ,bg-dark)))) + '(org-level-2 ((t (:weight normal :height 1.3 :slant italic :foreground ,bg-dark)))) + '(org-level-3 ((t (:weight normal :slant italic :height 1.2 :foreground ,bg-dark)))) + '(org-level-4 ((t (:weight normal :slant italic :height 1.1 :foreground ,bg-dark)))) '(org-level-5 ((t nil))) '(org-level-6 ((t nil))) '(org-level-7 ((t nil))) '(org-level-8 ((t nil))) - '(org-headline-done ((t (:family ,et-font)))) + ;; '(org-headline-done ((t (:family ,et-font)))) '(org-quote ((t nil))) '(org-block ((t (:background nil :height 0.9 :foreground ,bg-dark :family ,sans-mono-font)))) '(org-block-begin-line ((t (:background nil :height 0.8 :family ,sans-mono-font :foreground ,slate)))) @@ -1127,8 +1412,8 @@ ln -siv $(pwd)/tangle/spacemacs-light-customizations-theme.el ~/.emacs.d/ '(org-agenda-done ((t (:foreground ,doc)))) '(org-ellipsis ((t (:underline nil :foreground ,comment)))) '(org-tag ((t (:foreground ,doc)))) - '(org-table ((t (:family ,serif-mono-font :height 0.9 :background ,bg-white)))) - '(org-code ((t (:inherit nil :family ,serif-mono-font :foreground ,comment :height 0.9)))) + '(org-table ((t (:inherit fixed-pitch :height 0.9 :background ,bg-white)))) + '(org-code ((t (:inherit fixed-pitch :foreground ,comment :height 0.9)))) '(font-latex-sectioning-0-face ((t nil))) '(font-latex-sectioning-1-face ((t nil))) '(font-latex-sectioning-2-face ((t nil))) @@ -1399,6 +1684,33 @@ Remember where point is in a file. (kept-old-versions 2) (create-lockfiles nil)) #+end_src +** Local variables +#+begin_src emacs-lisp +(use-package files + :custom + <<files-custom>> + ) +#+end_src + +[[info:emacs#File Variables][File Variables]] are useful to ensure same behaviour in some files with different emacs configurations or to change behaviour from the default for one file. +Some settings could be harmful to emacs and the underlying system. Therefore many settings have to be declared as safe before using them. +#+begin_src emacs-lisp :tangle no :noweb-ref files-custom +(safe-local-variable-values + '((whitespace-style face trailing space-before-tab indentation empty space-after-tab newline-mark) + (whitespace-style face trailing space-before-tab indentation empty space-after-tab) + (eval set-window-buffer nil (current-buffer)) + (eval add-hook 'before-save-hook (lambda nil (fpi/tangle-async)) nil t) + (org-attach-preferred-new-method . dir) + (org-attach-use-inheritance . t) + (right-margin-width . 2) + (left-margin-width . 2) + (line-spacing . 0.2) + (after-save-hook org-babel-tangle) + (header-line-format . " ") + (after-save-hook . (org-babel-tangle)) + <<safe-local-variable-values>> +)) +#+end_src ** Personal keymap Unfortunately =C-c [a-z]= is not always a safe place for user-defined @@ -1462,8 +1774,29 @@ Goes backward if ARG is negative; error if CHAR not found." (backward-char) (forward-char)) (point)))) + <<simple-config>> :bind (:map global-map - ("M-z" . zap-up-to-char))) + ("M-z" . zap-up-to-char) + <<simple-bindings>> + )) +#+end_src +Use a hard ~keyboard-quit~. This is from Jeff Norden ([[https://lists.gnu.org/archive/html/emacs-devel/2020-07/msg00326.html][Message on emacs-devel]]). +#+begin_src emacs-lisp :tangle no :noweb-ref simple-config +(defun keyboard-quit-strong () + "Run `keyboard-quit' to return emacs to a more responsive state. +If repeated twice in a row, run `top-level' instead, to also exit +any recursive editing levels." + (interactive) + (when (eq last-command 'keyboard-quit-strong) + (setq this-command 'top-level) ;dis-arm a 3rd C-g + (ding) + (top-level)) + ;; Not reached after `top-level'. (A rare behavior in lisp.) + (keyboard-quit)) +#+end_src + +#+begin_src emacs-lisp :tangle no :noweb-ref simple-bindings +("C-g" . keyboard-quit-strong) #+end_src * Selection and search methods ** Completion frameworks @@ -1893,7 +2226,10 @@ confines of word boundaries (e.g. multiple words)." :hook (dired-mode . dired-hide-details-mode) (dired-mode . hl-line-mode) - (dired-mode . auto-revert-mode)) + (dired-mode . auto-revert-mode) + :bind (:map dired-mode-map + <<dired-bindings>> + )) (use-package find-dired :after dired @@ -2021,6 +2357,335 @@ of src_shell{getconf "PATH"}. See [[elisp:(describe-variable (add-to-list 'tramp-remote-path 'tramp-own-remote-path)) #+end_src ** Git +*** Git annex +There are some great ressources on [[https://git-annex.branchable.com/][git-annex]] integration in emacs in [[https://github.com/mm--/dot-emacs/blob/master/jmm-emacs.org][Josh's config]]. Most of my configuration is copied from there. +#+begin_src emacs-lisp +(use-package git-annex + :straight t + :config + <<git-annex-config>> + :bind + (:map git-annex-dired-map + <<git-annex-dired-bindings>>) + :after (dired)) +#+end_src +**** Actions to lock/unlock files +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("l" . git-annex-dired-lock-files) +("u" . git-annex-dired-unlock-files) +#+end_src +=git-annex.el= defines a handy macro to define generic =git-annex= CLI calls. +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-config +(git-annex-dired-do-to-files "lock" "Annex: locked %d file(s)") +(git-annex-dired-do-to-files "unlock" "Annex: unlocked %d file(s)") +#+end_src +**** Fix faces +=git-annex.el= kinda clobbers ~dired-marked-face~ and ~dired-flagged-face~. This fixes that. +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-config +(progn + (add-to-list 'dired-font-lock-keywords + (list "^[*].+ -> .*\\.git/annex/" + '("\\(.+\\)\\( -> .+\\)" (dired-move-to-filename) nil + (1 dired-marked-face) + (2 git-annex-dired-annexed-invisible)))) + (add-to-list 'dired-font-lock-keywords + (list "^[D].+ -> .*\\.git/annex/" + '("\\(.+\\)\\( -> .+\\)" (dired-move-to-filename) nil + (1 dired-flagged-face) + (2 git-annex-dired-annexed-invisible))))) +#+end_src +**** Make it easy to add metadata tags in git-annex +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("t" . jmm/dired-git-annex-tag) +#+end_src +Git-annex has a pretty cool ability to tag files and filter directory views based on metadata. It's kind of a pain to tag files, though, so here's a function that adds some autocompletion to tagging files. +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +(defvar-local jmm/git-annex-directory-tags nil + "Current git-annex tags set in the directory, as a list.") + +(defun jmm/dired-git-annex-current-tags (file-list &optional intersection) + "Get current git-annex tag for each file in FILE-LIST. With + optional argument INTERSECTION, only show tags all files share in common." + (let* ((metadata (with-output-to-string + (with-current-buffer + standard-output + (apply #'process-file "git" nil t nil "annex" "metadata" "--json" file-list)))) + (json-array-type 'list) + (jsonout (-map 'json-read-from-string (split-string metadata "\n" t)))) + (-reduce (if intersection '-intersection '-union) (--map (cdr (assoc 'tag (cdr (assoc 'fields it)))) jsonout)))) + +(defun jmm/dired-git-annex-tag (file-list tags &optional arg) + "Add git-annex TAGS to each file in FILE-LIST. +Used as an interactive command, prompt for a list of tags for all +files, showing the current tags all files currently have in common." + (interactive + (let* ((files (dired-get-marked-files t current-prefix-arg)) + (shared-tags (jmm/dired-git-annex-current-tags files t)) + ;; Cache directory tags + (current-tags (or jmm/git-annex-directory-tags + (setq jmm/git-annex-directory-tags + (or (jmm/dired-git-annex-current-tags '("--all")) '(""))))) + (crm-separator " ") + (crm-local-completion-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map crm-local-completion-map) + (define-key map " " 'self-insert-command) + map)) + (tags (completing-read-multiple + "Tags: " (--map (concat it crm-separator) current-tags) + nil nil + (when shared-tags (mapconcat 'identity shared-tags " "))))) + (setq jmm/git-annex-directory-tags (-union tags jmm/git-annex-directory-tags)) + (list files tags current-prefix-arg))) + (let ((args (cl-loop for x in tags + append (list "-t" x)))) + (-each file-list + (lambda (file) + (apply #'call-process "git" nil nil nil "annex" "metadata" (append args (list file))))) + (message (format "Tagged %d file(s)" (length file-list))))) +#+END_SRC +**** Mark unavailable files +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("*") +("* a" . jmm/dired-mark-git-annex-available-files) +("* u" . jmm/dired-mark-git-annex-unavailable-files) +#+end_src + +When you use this in combination with ~dired-do-kill-lines~ (by default bound to ~k~), it's easy to hide files that aren't present in the current annex repository. +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +(defun jmm/dired-mark-git-annex-unavailable-files () + "Mark git-annex files that are not present." + (interactive) + (dired-mark-if + (and (looking-at-p ".* -> \\(.*\\.git/annex/.+\\)") + (not (file-exists-p (file-truename (dired-get-filename t))))) + "unavailable file")) + +(defun jmm/dired-mark-git-annex-available-files () + "Mark git-annex files that are present." + (interactive) + (dired-mark-if + (and (looking-at-p ".* -> \\(.*\\.git/annex/.+\\)") + (file-exists-p (file-truename (dired-get-filename t)))) + "available file")) +#+END_SRC +**** Mark git-annex files with git-annex-matching-options +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref dired-bindings +("% a" . jmm/dired-mark-files-git-annex-matching) +#+END_SRC + +This command makes it easy to mark dired files using ~git-annex-matching-options~. + +For instance, you could find files that are in a certain remote using ~--in=remote~ or mark/unmark files that have a certain tag using ~--metadata tag=sometag~. +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +(defun jmm/dired-mark-files-git-annex-matching (matchingoptions &optional marker-char) + "Mark all files that match git annex's MATCHINGOPTIONS for use in later commands. +A prefix argument means to unmark them instead. +`.' and `..' are never marked." + (interactive + (list (read-string (concat (if current-prefix-arg "Unmark" "Mark") + " files matching (git annex match expression): ") + nil 'jmm-dired-annex-matchingoptions-history) + (if current-prefix-arg ?\040))) + (let ((dired-marker-char (or marker-char dired-marker-char))) + (dired-mark-if + (and (not (looking-at-p dired-re-dot)) + (not (eolp)) ; empty line + (let ((fn (dired-get-filename nil t))) + (when (and fn (not (file-directory-p fn))) + (message "Checking %s" fn) + (s-present? (shell-command-to-string + (mapconcat + #'identity + (list "git annex find" matchingoptions (shell-quote-argument fn)) + " ")))))) + "matching file"))) +#+END_SRC +**** Real file size +:PROPERTIES: +:header-args:emacs-lisp: :tangle no +:END: +Dired by default only shows the symlink file size. While it can be told to dereference symbolic links with the =-L= flag this only works on annexed files if they are present on the current machine. +Settings this flag causes more problems than it solves. Instead Josh has derived the functions below to determine the file size. I do not use them for now, but copied them here for future reference/usage. +***** Get git-annex file sizes +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("s" . jmm/dired-git-annex-print-human-file-size) +#+end_src +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +(defun jmm/git-annex-file-target (filename) + "If FILENAME is a git annex file, return its symlink target." + (-when-let (symname (and filename + (file-symlink-p filename))) + (when (string-match-p ".*\\.git/annex/.+" symname) + symname))) + +(defun jmm/dired-git-annex-file-target () + "If the dired file at point is a git annex file, return its symlink target." + (jmm/git-annex-file-target (dired-get-filename nil t))) + +(defun jmm/git-annex-file-size (filename) + "Try to determine the size of the git annex file FILENAME." + (-when-let (target (jmm/git-annex-file-target filename)) + (or (save-match-data + (when (string-match "SHA256E-s\\([0-9]+\\)--" target) + (string-to-number (match-string 1 target)))) + (-some-> (expand-file-name target (file-name-directory filename)) + file-attributes + file-attribute-size)))) + +(defun jmm/dired-git-annex-print-human-file-size () + "Try to print the human readable file size of the dired git-annex file at point." + (interactive) + (let* ((filename (dired-get-filename nil t)) + (string-file (file-name-nondirectory filename))) + (-if-let (filesize (-some-> (jmm/git-annex-file-size filename) + file-size-human-readable)) + (message "%s - %s" filesize string-file) + (message "Can't determine git annex file size of %s" string-file)))) +#+END_SRC +***** Show git-annex file sizes in dired +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("S" . jmm/dired-git-annex-add-real-file-sizes) +#+end_src + +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +;; Based off of `dired--align-all-files' +(defun jmm/dired-git-annex-add-real-file-sizes () + "Go through all the git-annex files in dired, replace the +symlink file size with the real file size, then try to align +everything." + (interactive) + (require 'dired-aux) + (let ((regexp directory-listing-before-filename-regexp)) + (save-excursion + (goto-char (point-min)) + (dired-goto-next-file) + (while (or (dired-move-to-filename) + (progn (save-restriction + (narrow-to-region (dired-subdir-min) (dired-subdir-max)) + (dired--align-all-files)) + (dired-next-subdir 1 t) + (dired-goto-next-file) + (dired-move-to-filename))) + (let ((inhibit-read-only t)) + (when (and (jmm/dired-git-annex-file-target) + (re-search-backward regexp (line-beginning-position) t)) + (goto-char (match-beginning 0)) + (-when-let (newsize (-some-> (jmm/git-annex-file-size (dired-get-filename nil t)) + file-size-human-readable)) + (search-backward-regexp "[[:space:]]" nil t) + (when (re-search-forward "[[:space:]]+\\([^[:space:]]+\\)[[:space:]]" nil t) + (goto-char (match-beginning 1)) + (delete-region (point) (match-end 1)) + (insert-and-inherit newsize)))) + (forward-line)))))) +#+END_SRC + +#+BEGIN_SRC emacs-lisp :tangle no +;; (add-hook 'dired-mode-hook #'jmm/dired-git-annex-add-real-file-sizes) +;; (add-hook 'dired-after-readin-hook #'jmm/dired-git-annex-add-real-file-sizes) +#+END_SRC +***** Sort dired by file size +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref git-annex-config +(defun jmm/dired-dir-files-beginning () + "First point where there's a filename on the line. Beginning of line." + (save-excursion + (goto-char (dired-subdir-min)) + (dired-goto-next-file) + (beginning-of-line) + (point))) + +(defun jmm/dired-dir-files-end () + "Last point where there's a filename. End of line." + (save-excursion + (goto-char (dired-subdir-max)) + (while (not (dired-get-filename nil t)) + (dired-previous-line nil)) + (end-of-line) + (point))) + +(defun jmm/dired-file-size () + "Return the file size of a file at point (for sorting). Takes +into account git-annex files." + (let* ((filename (dired-get-filename nil t)) + (string-file (file-name-nondirectory filename))) + (or (jmm/git-annex-file-size filename) + (file-attribute-size (file-attributes filename))))) + +;; TODO: Should just try to directly use the field listed. +(defun jmm/dired-sort-size (&optional ascending) + "Sort some dired lines by size (consider annex sizes). +With optional argument ASCENDING, sort by ascending file size. (I +like going the other way around usually.)" + (interactive "P") + (let (buffer-read-only + (beg (jmm/dired-dir-files-beginning)) + (end (jmm/dired-dir-files-end))) + (save-excursion + (save-restriction + (narrow-to-region beg end) + (goto-char (point-min)) + (sort-subr (not ascending) + 'forward-line 'end-of-line + #'jmm/dired-file-size nil))))) +#+END_SRC +**** Browsing URLs for git-annex files +#+begin_src emacs-lisp :tangle no :noweb-ref git-annex-dired-bindings +("b" . jmm/git-annex-browse-url) +#+end_src +#+BEGIN_SRC emacs-lisp +;; TODO: Process multiple files at once? +(defun jmm/git-annex-whereis-info (filename) + "Get information about where a git-annex file exists. +Returns a parsed json list from whereis." + (let* ((json-array-type 'list) + (whereisdata (shell-command-to-string + (mapconcat + #'identity + (list "git annex whereis --json" (shell-quote-argument filename)) + " ")))) + (when (s-present? whereisdata) + (json-read-from-string whereisdata)))) + +(defun jmm/git-annex-urls (filename) + "Get the git-annex web urls for FILENAME." + (-some->> (jmm/git-annex-whereis-info filename) + (assoc-default 'whereis) + (-mapcat (lambda (x) (assoc-default 'urls x))) + (-map (lambda (s) (s-chop-prefix "yt:" s))))) + +(defun jmm/git-annex-browse-url () + "Browse the first git-annex web urls for file at point." + (interactive) + (let* ((filename (dired-get-filename nil t)) + (filestr (file-name-nondirectory filename))) + (-if-let (url (car (jmm/git-annex-urls filename))) + (progn + (message "Opening url: %s" url) + (jmm/org-open-link-alternate-browser #'browse-url url)) + (user-error "No url found for %s" filestr)))) +#+END_SRC +**** Eshell helper functions +Helper functions to open dired view from eshell or list =git-annex= files which match a search. +#+BEGIN_SRC emacs-lisp +(defun jmm/git-annex-find-files (&rest args) + "Generate a list of git annex files that match ARGS. +For example, ARGS could be \"--in=here\"" + (-remove #'s-blank? + (s-split "\0" + (shell-command-to-string (mapconcat #'identity + (append '("git annex find --print0") args) + " "))))) +(defun eshell/dga (&rest args) + "Show a `dired' buffer of git annex files that match ARGS. +For example, ARGS could be \"--in=here\"" + (dired (cons "." (apply #'jmm/git-annex-find-files args)))) + +(defun eshell/gaf (&rest args) + "Return a list of git annex files that match ARGS. +For example, ARGS could be \"--in=here\"" + (apply #'jmm/git-annex-find-files args)) +#+END_SRC *** Magit #+BEGIN_SRC emacs-lisp (use-package magit @@ -2132,15 +2797,12 @@ some safe local variable values. :delight :straight t :custom - (gac-automatically-push-p nil) - :config - (add-to-list 'safe-local-variable-values - '(eval add-hook - (quote after-save-hook) - (quote gac-after-save-func) - t t)) - (add-to-list 'safe-local-variable-values - '(git-auto-commit-mode . t))) + (gac-automatically-push-p nil)) +#+end_src + +#+begin_src emacs-lisp :tangle no :noweb-ref safe-local-variable-values +(git-auto-commit-mode . t) +(gac-debounce-interval . 600) #+end_src ** Projectile @@ -2374,8 +3036,12 @@ Advice =load-theme= to update the colors for =pdf-view-midnight-mode= after the theme changes. #+NAME: theme-dependent-vars #+begin_src emacs-lisp :tangle no -(defadvice load-theme (after update-pdf-view-midnight-color activate) - (customize-save-variable 'pdf-view-midnight-colors `(,(face-attribute 'default :foreground) . ,(face-attribute 'default :background)))) +(defun update-pdf-view-midnight-color (&rest arg) + (customize-save-variable + 'pdf-view-midnight-colors + `(,(face-attribute 'default :foreground) . ,(face-attribute 'default :background)))) +(advice-add 'load-theme :after + #'update-pdf-view-midnight-color) #+end_src ** Latex @@ -2491,34 +3157,18 @@ Hansen's]] configs. ("omap" . "http://nominatim.openstreetmap.org/search?q=%s&polygon=1"))) (org-ellipsis " ") (org-outline-path-complete-in-steps nil) - (org-log-state-notes-into-drawer "NOTES") + (org-log-into-drawer "NOTES") (org-clock-into-drawer "LOGBOOK") - (org-log-done 'time) - (org-log-redeadline 'time) - (org-log-reschedule 'time) - (org-todo-keywords '((sequence "PLANNING(p)" "NEXT(n)" "INPROGRESS(i!)" "WAITING(w@/!)" "|" "ICEBOX(x@)" "DONE(d)") - (sequence "S(s)" "DONE(d)") - (sequence "PHONE(P)" "MEETING(m)" "|" "CANCELLED(c)") - (sequence "TODO(t)" "|" "DONE(d)") - (sequence "IDLE(a)"))) - (org-use-fast-todo-selection t) - (org-todo-keyword-faces - '(("NEXT" :foreground "light blue" :weight bold) - ("INPROGRESS" :foreground "burlywood" :weight bold) - ("DONE" :foreground "forest green" :weight bold) - ("WAITING" :foreground "orange" :weight bold) - ("ICEBOX" :foreground "orange" :weight normal) - ("CANCELLED" :foreground "forest green" :weight bold) - ("MEETING" :foreground "yellow" :weight bold) - ("PHONE" :foreground "yellow" :weight bold) - ("IDLE" :foreground "magenta" :weight bold))) (org-clock-in-switch-to-state 'bh/clock-in-to-inprogress) (org-tags-column 0) + (org-tags-exclude-from-inheritance '( + <<org-custom-no-inheritance-tags>> + )) <<org-custom>> :config (add-hook 'org-mode-hook 'turn-on-org-cdlatex) (add-to-list 'org-structure-template-alist (cons "f" "figure")) - (add-to-list 'org-tags-exclude-from-inheritance "MARKED") + ;; (add-to-list 'org-tags-exclude-from-inheritance "MARKED") (defun bh/clock-in-to-inprogress (kw) "Switch a task from NEXT to INPROGRESS when clocking in. Skips capture tasks, projects, and subprojects. @@ -2540,7 +3190,9 @@ Switch projects and subprojects from NEXT back to TODO" (use-package org-indent :delight :custom - (org-startup-indented t)) + (org-startup-indented t) + <<org-indent-custom>> + ) #+end_src #+begin_src emacs-lisp (use-package ob @@ -2597,7 +3249,7 @@ Switch projects and subprojects from NEXT back to TODO" ) (use-package org-src :custom - (org-src-window-setup 'current-window) + (org-src-window-setup 'other-window) (org-src-fontify-natively t) (org-src-tab-acts-natively t) (org-edit-src-content-indentation 0)) @@ -2628,55 +3280,12 @@ Switch projects and subprojects from NEXT back to TODO" ;; See emacs.christianbaeuerlein.com/my-org-config.html (org-agenda-block-separator 9472) (org-agenda-custom-commands - `(("d" "Day agenda" - ((agenda "" ((org-agenda-span 'day))) - (org-time-budgets-in-agenda-maybe) - (tags-todo "/+INPROGRESS" - ((org-agenda-overriding-header "Active Tasks"))))) - ("w" . "Week agendas") - ("ww" "Standard week agenda" - ((agenda "" ((org-agenda-span 'week))))) - ("wn" "Next Week's agenda" - ((agenda "" ((org-agenda-span 'week) - (org-agenda-start-day "mon"))) - (tags-todo "+work"))) - ("n" "Agenda and all TODOs" - ((todo "INPROGRESS" - ((org-agenda-overriding-header "Inprogress Tasks"))) - (agenda) - (tags-todo "+soon+LEVEL=2" - ((org-agenda-overriding-header "2nd Level /Soon/ Tasks"))) - (tags-todo "+soon" - ((org-agenda-overriding-header "All /Soon/ Tasks"))) - (tags-todo "+shelve") - (tags-todo "+habit") - (todo "IDLE") - (tags-todo "-habit-shelve-soon-idle"))) - ("r" "Refile entries" ((tags "+REFILE"))) - ("i" "Idle Actions" - ((tags-todo "IDLE-READLIST-WATCH" - ((org-agenda-overriding-header "Idle Tasks") - (org-agenda-skip-function 'bh/skip-project-tasks) - (org-agenda-sorting-strategy - '(todo-state-down effort-up)))) - (tags-todo "READLIST" - ((org-agenda-overriding-header "Idle Reading List") - (org-agenda-sorting-strategy - '(todo-state-down effort-up)))) - (tags-todo "WATCH" - ((org-agenda-overriding-header "Things to Watch") - (org-agenda-skip-function 'bh/skip-project-tasks) - (org-agenda-sorting-strategy - '(todo-state-down effort-up)))))) - ("z" "Todos in org-roam-dir" - ((alltodo "" - ((org-agenda-files (fpi/collect-org-directories-recursively org-roam-directory)))))) - ("c" "Agenda and all todos in current directory" - ((agenda "" - ((org-agenda-files (fpi/collect-org-directories-recursively default-directory)))) - (alltodo "" - ((org-agenda-files (fpi/collect-org-directories-recursively default-directory)))))))) - ) + `( + <<org-agenda-custom-commands>> + ))) +#+end_src + +#+begin_src emacs-lisp (use-package ob-core :custom (org-confirm-babel-evaluate nil)) @@ -2686,6 +3295,7 @@ Switch projects and subprojects from NEXT back to TODO" (use-package ol-notmuch) (use-package org-habit) #+end_src + #+begin_src emacs-lisp (use-package org-inlinetask) #+end_src @@ -2712,6 +3322,275 @@ Use imagemagick and standalone class for latex preview. (use-package org-num :delight) #+end_src +*** Task organization +**** Todo settings +- WAITING tasks are waiting on the completion of other tasks +- NEXT tasks can be picked up +- INPROGRESS are current tasks with time clocked +- DONE are complete tasks +- ICEBOX tasks are on ice for whatever reason + +TODO->DONE cycle is for habits.\\ +Idle states cover things to do for time in between, checking the +inbox, reading news, … + +Phonecalls? + +#+BEGIN_SRC dot :file /tmp/todo.png +digraph hierarch{ + node [shape=box] + // Tasks, Projects + PLANNING -> NEXT, INPROGRESS, ICEBOX + WAITING -> NEXT -> INPROGRESS -> DONE, WAITING, ICEBOX + NEXT -> WAITING -> INPROGRESS, ICEBOX + NEXT -> ICEBOX, DONE + + // stuff for idle time + IDLE -> IDLE + //NEXT -> DONE + + // Phonecalls, Meetings + PHONE -> DONE, CANCELED + MEETING -> DONE, CANCELED +} +#+END_SRC + +#+RESULTS: +[[file:/tmp/todo.png]] + +#+begin_src emacs-lisp :noweb-ref org-custom :tangle no +(org-todo-keywords '((sequence "HOLD(h)" "NEXT(n)" "INPROGRESS(i!)" "WAITING(w@/!)" "|" "ICEBOX(x@)" "DONE(d)") ;;todos + (sequence "PLANNING(p)" "TODO(t)" "ACTIVE(a)" "|" "CANCELLED(c)" "DONE(d)") ;;projects + (sequence "PHONE(P)" "MEETING(m)" "|" "CANCELLED(c)" "DONE(d)") + (sequence "TODO(t)" "|" "DONE(d)") ;;habits + (sequence "IDLE(b)"))) +(org-use-fast-todo-selection t) +(org-todo-keyword-faces + '(("HOLD" :foreground "light gray" :weight bold) + ("NEXT" :foreground "light blue" :weight bold) + ("INPROGRESS" :foreground "burlywood" :weight bold) + ("ACTIVE" :foreground "chocolate" :weight bold) + ("DONE" :foreground "forest green" :weight bold) + ("WAITING" :foreground "orange" :weight bold) + ("ICEBOX" :foreground "orange" :weight normal) + ("CANCELLED" :foreground "forest green" :weight bold) + ("MEETING" :foreground "yellow3" :weight bold) + ("PHONE" :foreground "yellow3" :weight bold) + ("IDLE" :foreground "magenta" :weight bold))) +#+end_src + +Switch a todo entry from NEXT to INPROGRESS when clocking in. +#+begin_src emacs-lisp :tangle no +(setq org-clock-in-switch-to-state 'bh/clock-in-to-inprogress) +(defun bh/clock-in-to-inprogress (kw) + "Switch a task from NEXT to INPROGRESS when clocking in. +Skips capture tasks, projects, and subprojects. +Switch projects and subprojects from NEXT back to TODO" + (when (not (and (boundp 'org-capture-mode) org-capture-mode)) + (cond + ((and (member (org-get-todo-state) (list "NEXT")) + (bh/is-task-p)) + "INPROGRESS") + ((and (member (org-get-todo-state) (list "NEXT")) + (bh/is-project-p)) + "INPROGRESS")))) +#+end_src +***** State changes +Track state changes to done & changes to schedules and deadlines. +#+begin_src emacs-lisp :tangle no :noweb-ref org-custom +(org-log-done 'time) +(org-log-redeadline 'time) +(org-log-reschedule 'time) +#+end_src +**** Org agenda custom commands +***** Task-related agendas +Simple day agenda with =INPROGRESS= tasks +#+begin_src emacs-lisp :tangle no :noweb-ref org-agenda-custom-commands +("d" "Day agenda" + ((agenda "" ((org-agenda-span 'day))) + (org-time-budgets-in-agenda-maybe) + (tags-todo "/+INPROGRESS" + ((org-agenda-overriding-header "Active Tasks"))))) +#+end_src +Agenda with all open tasks +#+begin_src emacs-lisp :tangle no :noweb-ref org-agenda-custom-commands +("n" "Agenda and all TODOs" + ((todo "INPROGRESS" + ((org-agenda-overriding-header "Inprogress Tasks"))) + (agenda) + (tags-todo "+soon+LEVEL=2" + ((org-agenda-overriding-header "2nd Level /Soon/ Tasks"))) + (tags-todo "+soon" + ((org-agenda-overriding-header "All /Soon/ Tasks"))) + (tags-todo "+shelve") + (tags-todo "+habit") + (todo "IDLE") + (tags-todo "-habit-shelve-soon-idle"))) +#+end_src +****** Fancy agenda to choose todays tasks +Based on https://github.com/psamim/dotfiles/blob/master/doom/config.el#L73. +#+begin_src emacs-lisp :tangle no :noweb-ref org-agenda-custom-commands +("o" "My Agenda" + ((agenda "" ((org-agenda-block-separator ? ) + (org-agenda-overriding-header "\n⚡ Deadlines:\n⎺⎺⎺⎺⎺⎺⎺⎺⎺") + (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)) + (org-agenda-format-date (lambda (date) "")))) + (agenda* "" ((org-agenda-block-separator ? ) + (org-agenda-skip-function '(fpi/org-agenda-skip-if-not-today)) + (org-deadline-warning-days 0) + (org-agenda-todo-ignore-timestamp 'all) + (org-agenda-start-day "+0d") + (org-agenda-span 1) + (org-agenda-overriding-header "⚡ Schedule:\n⎺⎺⎺⎺⎺⎺⎺⎺⎺") + (org-agenda-repeating-timestamp-show-all nil) + (org-agenda-remove-tags t) + (org-agenda-use-time-grid t) + ;; (org-agenda-prefix-format " %-3i %30b %t%s") + (org-agenda-todo-keyword-format " ☐ ") + (org-agenda-current-time-string "⮜┈┈┈┈┈┈┈ now") + (org-agenda-scheduled-leaders '("" "")) + (org-agenda-time-grid (quote ((daily today remove-match) + (0900 1200 1500 1800 2100) + " " "┈┈┈┈┈┈┈┈┈┈┈┈┈"))))) + (org-time-budgets-in-agenda-maybe) + (agenda "" ((org-agenda-block-separator ? ) + (org-agenda-overriding-header "\n⚡ Scheduled Tasks:\n⎺⎺⎺⎺⎺⎺⎺⎺⎺") + (org-agenda-skip-function '(org-agenda-skip-entry-if 'notscheduled)) + (org-agenda-use-time-grid nil) + (org-agenda-format-date (lambda (date) "")))) + )) +#+end_src +#+begin_src emacs-lisp +(defun fpi/org-agenda-skip-if-not-today () + "Skip all scheduled entries and deadlines not due for today." + (let ((subtree-end (save-excursion (org-end-of-subtree t))) + (deadline-day + (when (org-entry-get nil "DEADLINE") + (time-to-days + (org-time-string-to-time + (org-entry-get nil "DEADLINE"))))) + (scheduled-day + (when (org-entry-get nil "SCHEDULED") + (time-to-days + (org-time-string-to-time + (org-entry-get nil "SCHEDULED"))))) + (now (time-to-days (current-time)))) + (and (or (and deadline-day + (not (= deadline-day now))) + (and scheduled-day + (not (= scheduled-day now)))) + subtree-end))) +#+end_src +***** Week agendas +#+begin_src emacs-lisp :tangle no :noweb-ref org-agenda-custom-commands +("w" . "Week agendas") +("ww" "Standard week agenda" + ((agenda "" ((org-agenda-span 'week))))) +("wn" "Next Week's agenda" + ((agenda "" ((org-agenda-span 'week) + (org-agenda-start-day "mon"))) + (tags-todo "+work"))) +#+end_src +***** Misc agendas +#+begin_src emacs-lisp :tangle no :noweb-ref org-agenda-custom-commands +("r" "Refile entries" ((tags "+REFILE"))) +("i" "Idle Actions" + ((tags-todo "IDLE-READLIST-WATCH" + ((org-agenda-overriding-header "Idle Tasks") + (org-agenda-skip-function 'bh/skip-project-tasks) + (org-agenda-sorting-strategy + '(todo-state-down effort-up)))) + (tags-todo "READLIST" + ((org-agenda-overriding-header "Idle Reading List") + (org-agenda-sorting-strategy + '(todo-state-down effort-up)))) + (tags-todo "WATCH" + ((org-agenda-overriding-header "Things to Watch") + (org-agenda-skip-function 'bh/skip-project-tasks) + (org-agenda-sorting-strategy + '(todo-state-down effort-up)))))) +("z" "Todos in org-roam-dir" + ((alltodo "" + ((org-agenda-files (fpi/collect-org-directories-recursively org-roam-directory)))))) +("c" "Agenda and all todos in current directory" + ((agenda "" + ((org-agenda-files (fpi/collect-org-directories-recursively default-directory)))) + (alltodo "" + ((org-agenda-files (fpi/collect-org-directories-recursively default-directory)))))) +#+end_src +**** Refile +Use the full outline path so I can distinguish headlines with the same name & disable step-wise completion as I think from the refile target backwards, not from top-level downwards. Also include the current file's headings as a refile targets up to a deep level, all agenda files up to a small level and all open org files up to an even smaller level. + +As refile only works on file-visiting buffers, we need to filter all other org buffers from ~(org-buffer-list)~. +#+begin_src emacs-lisp +(defun fpi/org-file-buffer-list () + "Return a list of org buffers which visit files." + (seq-filter 'buffer-file-name (org-buffer-list))) +#+end_src + +#+begin_src emacs-lisp :noweb-ref org-custom :tangle no +(org-refile-use-outline-path 'file) +(org-refile-targets '((buffer-file-name :maxlevel . 12) + (org-agenda-files :maxlevel . 10) + (fpi/org-file-buffer-list :maxlevel . 2))) +#+end_src +**** Time budgets +Gives an overview of time spent on defined budgets this week. Great to track if you've worked enough hours. To use it add ~(org-time-budgets-in-agenda-maybe)~ after ~(agenda)~ in a custom agenda command. +#+begin_src emacs-lisp +(use-package org-time-budgets + :straight (:host github :repo "fpiper/org-time-budgets" + :branch "develop" + :no-byte-compile t) + :custom + (org-time-budgets '((:title "Work" :match "+work-nowork" :budget "40:00" :blocks (workday week)) + (:title "├Research" :match "+work+research" :budget "24:00" :blocks (nil week)) + (:title "╰Teaching" :match "+work+teaching" :budget "8:00" :blocks (nil week))))) +#+end_src +**** Column view +#+begin_src emacs-lisp +(setq org-columns-default-format + "%50ITEM(Task) %5Effort(Effort){:} %5CLOCKSUM %3PRIORITY %20DEADLINE %20SCHEDULED %20TIMESTAMP %TODO %CATEGORY %TAGS") +#+end_src +**** Clocking +***** Combine adjacent clock lines +#+begin_src emacs-lisp +(defun fpi/org-clock-join-last-clock () + "Join current clock with last one if start/end point match." + (save-mark-and-excursion + (beginning-of-line) + (let* ((eol (save-excursion (end-of-line) (point))) + (boi (progn (re-search-forward "\\[" eol t) (backward-char) (point))) + (eoi (progn (re-search-forward "\\]" eol t) (point))) + (i (buffer-substring-no-properties boi eoi)) ;; last clock-in-time + (boc (progn (re-search-forward "\\[" eol t) (backward-char) (point))) + (eoc (progn (re-search-forward "\\]" eol t) (point))) + (c (buffer-substring-no-properties boc eoc))) ;; last clock-out-time (equals org-clock-out-time if last clock) + (next-line) + (end-of-line) + (let* ((bol (save-excursion (beginning-of-line) (point))) + (eoo (progn (re-search-backward "\\]" bol t) (forward-char) (point))) + (boo (progn (re-search-backward "\\[" bol t) (point))) + (o (buffer-substring-no-properties boo eoo))) ;; last-last clock-out-time + (when (equal i o) + (delete-region boo eoo) + ;; (insert (format-time-string (org-time-stamp-format t t) org-clock-out-time)) + (insert c) + (org-evaluate-time-range) + (previous-line) + (delete-region (save-excursion (beginning-of-line) (backward-char) (point)) eol) + (message (format "Joined nearby clocks at %s" i))))))) +(add-hook 'org-clock-out-hook 'fpi/org-clock-join-last-clock) +#+end_src +***** org-clock-convenience +#+begin_src emacs-lisp +(use-package org-clock-convenience + :straight t + :bind (:map org-agenda-mode-map + ("<S-up>" . org-clock-convenience-timestamp-up) + ("<S-down>" . org-clock-convenience-timestamp-down) + ("<C-right>" . org-clock-convenience-fill-gap) + ("<C-left>" . org-clock-convenience-fill-gap-both))) +#+end_src *** org-checklist #+begin_quote This file provides some functions for handing repeated tasks which involve @@ -2727,6 +3606,20 @@ print the list. :after org :straight (org-plus-contrib)) #+end_src +*** Handling web urls +**** org-web-tools +:PROPERTIES: +:ID: dc4129ff-6d76-4f12-926f-c62a687a39ec +:END: +This provides functions to get webpage title or content for org mode links. +#+begin_src emacs-lisp +(use-package org-web-tools + :straight t) +#+end_src +#+begin_src emacs-lisp :noweb-ref fpi-bindings :tangle no +(define-key fpi-map "l" #'org-web-tools-insert-link-for-url) +#+end_src + *** Gnorb :PROPERTIES: :ID: 990e2668-11d6-45eb-9c9b-1dc0b89b556d @@ -2798,7 +3691,11 @@ Default keybindings: '(progn (define-key message-mode-map (kbd "C-c t") #'gnorb-gnus-outgoing-do-todo))) #+end_example - +**** More refile targets +Make gnorb consider the same refile targets as org. +#+begin_src emacs-lisp :tangle no :noweb-ref gnorb-custom +(gnorb-gnus-trigger-refile-targets org-refile-targets) +#+end_src *** Inline images Resize inline images to 400px but respect width specifications in attribute lines. @@ -2813,38 +3710,6 @@ Also display remote images by downloading them. #+begin_src emacs-lisp :noweb-ref ob-hooks :tangle no (org-babel-after-execute . org-display-inline-images) #+end_src -*** Refile -Use the full outline path so I can distinguish headlines with the same name & disable step-wise completion as I think from the refile target backwards, not from top-level downwards. Also include the current file's headings as a refile targets up to a deep level, all agenda files up to a small level and all open org files up to an even smaller level. - -As refile only works on file-visiting buffers, we need to filter all other org buffers from ~(org-buffer-list)~. -#+begin_src emacs-lisp -(defun fpi/org-file-buffer-list () - "Return a list of org buffers which visit files." - (seq-filter 'buffer-file-name (org-buffer-list))) -#+end_src - -#+begin_src emacs-lisp :noweb-ref org-custom :tangle no -(org-refile-use-outline-path 'file) -(org-refile-targets '((buffer-file-name :maxlevel . 8) - (org-agenda-files :maxlevel . 5) - (fpi/org-file-buffer-list :maxlevel . 2))) -#+end_src -*** Time budgets -Gives an overview of time spent on defined budgets this week. Great to track if you've worked enough hours. To use it add ~(org-time-budgets-in-agenda-maybe)~ after ~(agenda)~ in a custom agenda command. -#+begin_src emacs-lisp -(use-package org-time-budgets - :straight (:host github :repo "fpiper/org-time-budgets" - :branch "develop") - :custom - (org-time-budgets '((:title "Work" :match "+work-nowork" :budget "40:00" :block workweek) - (:title "Research" :match "+work+research" :budget "24:00" :block total-only) - (:title "Teaching" :match "+work+teaching" :budget "8:00" :block total-only)))) -#+end_src -*** Column view -#+begin_src emacs-lisp -(setq org-columns-default-format - "%50ITEM(Task) %5Effort(Effort){:} %5CLOCKSUM %3PRIORITY %20DEADLINE %20SCHEDULED %20TIMESTAMP %TODO %CATEGORY %TAGS") -#+end_src *** org-caldav #+begin_src emacs-lisp (use-package org-caldav @@ -2856,48 +3721,23 @@ Gives an overview of time spent on defined budgets this week. Great to track if (org-caldav-files nil) (org-caldav-sync-direction 'cal->org) (org-caldav-delete-calendar-entries 'never) - (org-caldav-exclude-tags '(nocal)) + (org-caldav-exclude-tags nil) ) #+end_src -*** Clocking -**** Combine adjacent clock lines +*** Babel +This function is handy to use in header arguments to create names based on the current org heading. E.g. =:var data=(fpi/format-headline "/tmp/prefix_")= #+begin_src emacs-lisp -(defun fpi/org-clock-join-last-clock () - "Join current clock with last one if start/end point match." - (save-mark-and-excursion - (beginning-of-line) - (let* ((eol (save-excursion (end-of-line) (point))) - (boi (progn (re-search-forward "\\[" eol t) (backward-char) (point))) - (eoi (progn (re-search-forward "\\]" eol t) (point))) - (i (buffer-substring-no-properties boi eoi)) ;; last clock-in-time - (boc (progn (re-search-forward "\\[" eol t) (backward-char) (point))) - (eoc (progn (re-search-forward "\\]" eol t) (point))) - (c (buffer-substring-no-properties boc eoc))) ;; last clock-out-time (equals org-clock-out-time if last clock) - (next-line) - (end-of-line) - (let* ((bol (save-excursion (beginning-of-line) (point))) - (eoo (progn (re-search-backward "\\]" bol t) (forward-char) (point))) - (boo (progn (re-search-backward "\\[" bol t) (point))) - (o (buffer-substring-no-properties boo eoo))) ;; last-last clock-out-time - (when (equal i o) - (delete-region boo eoo) - ;; (insert (format-time-string (org-time-stamp-format t t) org-clock-out-time)) - (insert c) - (org-evaluate-time-range) - (previous-line) - (delete-region (save-excursion (beginning-of-line) (backward-char) (point)) eol) - (message (format "Joined nearby clocks at %s" i))))))) -(add-hook 'org-clock-out-hook 'fpi/org-clock-join-last-clock) +(defun fpi/format-headline (&optional pre post) + (let ((pre (or pre "")) + (post (or post ""))) + (format "%s%s%s" pre (nth 4 (org-heading-components)) post))) #+end_src -**** org-clock-convenience +*** Durations #+begin_src emacs-lisp -(use-package org-clock-convenience - :straight t - :bind (:map org-agenda-mode-map - ("<S-up>" . org-clock-convenience-timestamp-up) - ("<S-down>" . org-clock-convenience-timestamp-down) - ("<C-right>" . org-clock-convenience-fill-gap) - ("<C-left>" . org-clock-convenience-fill-gap-both))) +(use-package org-duration + :after org + :custom + (org-duration-format '(("h" . t) ("min" . t) (special . h:mm)))) #+end_src *** ox-reveal #+BEGIN_SRC emacs-lisp @@ -2961,17 +3801,12 @@ Fix for hanging emacs. The original function calls file-exist-p which opens a sl :PROPERTIES: :ID: fd3936c7-9fc5-42d0-990d-32024e23b22f :END: -=Org-edna= is a great tool to manage =TODO= dependencies. I mainly use -it to mark tasks as =NEXT= after switching another task to =DONE=. The -functions below are taken from Josh's Emacs Config over at [[https://github.com/mm--/dot-emacs/blob/master/jmm-org-config.org][Github]]. He -wrote wrote a =edna-finder= which allows link descriptions and a nice -hydra to manage the various =org-edna= properties. I call it in my -[[id:22750e48-aaee-4f60-bdce-1d511ebe3375][context aware hydra]] when on an org headline. For more functions and -explanations checkout his config. +=Org-edna= is a great tool to manage =TODO= dependencies. I mainly use it to mark tasks as =NEXT= after switching another task to =DONE=. The functions below are taken from Josh's Emacs Config over at [[https://github.com/mm--/dot-emacs/blob/master/jmm-org-config.org][Github]]. He wrote a =edna-finder= which allows link descriptions and a nice hydra to manage the various =org-edna= properties. I call it in my [[id:22750e48-aaee-4f60-bdce-1d511ebe3375][context aware hydra]] when on an org headline. For more functions and explanations checkout his config. #+begin_src emacs-lisp (use-package org-edna :straight t :after org + :delight :config (org-edna-load) (defun org-edna-finder/link-ids (&rest ids) @@ -3054,7 +3889,6 @@ content syncing upon commit. (org-attach-git-annex-cutoff 0)) #+end_src *** Org-Capture -Templates #+BEGIN_SRC emacs-lisp (use-package org-capture :custom @@ -3066,25 +3900,38 @@ Templates '( <<org-capture-templates-contexts>>)))) #+END_SRC + **** Templates +:PROPERTIES: +:header-args:emacs-lisp: :eval never :noweb yes +:END: ***** Journal Capture templates for journal entries. Mostly to just keep track of things I have looked at and which may be interesting later, but do not warrant a zettel right now. #+BEGIN_SRC emacs-lisp :tangle no :noweb-ref org-capture-templates ("j" "Journal") -("jj" "Journal Entry (Link)" +("jj" "Link Current buffer" entry (file+olp+datetree ,org-journal-file) ;; "** %<%H:%M> %a\n %i%? \n%:description\n%:elfeed-entry-content\n%:elfeed-entry-date\n%:elfeed-entry-meta\n%:elfeed-entry-title\n%:elfeed-entry-enclosures\n%:elfeed-entry-tags" ) "** %<%H:%M> %a %i%?" ) -("je" "Journal Entry" +("je" "Manual Entry" entry (file+olp+datetree ,org-journal-file) "** %<%H:%M> %? %i" ) #+END_SRC +To get the title from the url in =kill-ring= I use [[id:dc4129ff-6d76-4f12-926f-c62a687a39ec][org-web-tools]]. +#+BEGIN_SRC emacs-lisp :tangle no :noweb-ref org-capture-templates +("jw" "Web Link from kill-ring" + entry + (file+olp+datetree + ,org-journal-file) + "** %<%H:%M> %(org-web-tools--org-link-for-url (org-web-tools--get-first-url))%? +%i") +#+END_SRC ***** Interrupts For interruptions. These are saved in a global refile file and to be sorted to their appropriate place. #+BEGIN_SRC emacs-lisp :tangle no :noweb-ref org-capture-templates @@ -3210,7 +4057,8 @@ Templates I no longer use, but may be interesting. "* %i%? %(and (org-id-get-create) nil) :PROPERTIES:\n:CREATED: %u\n:END:\n") #+END_SRC -**** Setup for floating capture window. For reference see [[https://www.windley.com/archives/2010/12/capture_mode_and_emacs.shtml][here]]. +**** Setup for floating capture window +For reference see [[https://www.windley.com/archives/2010/12/capture_mode_and_emacs.shtml][here]]. #+begin_src emacs-lisp (defun fpi/make-floating-frame (&optional width height minibuffer name) (interactive) @@ -3326,12 +4174,26 @@ A small function to toggle the encryption state of the current entry. #+begin_src emacs-lisp (use-package org-ref :straight t + :bind (:map org-mode-map (("C-c n p" . fpi/open-bibtex-pdf-or-directory))) :custom (org-ref-bibliography-notes nil) (org-ref-notes-function 'org-ref-notes-function-many-files) (org-ref-notes-directory "~/git/projects/zettel/Lit") (org-ref-default-bibliography '("~/git/projects/personal/bib.bib")) - (org-ref-pdf-directory "~/git/projects/personal/Lit/")) + (org-ref-pdf-directory "~/git/projects/personal/Lit/") + :config + (defun fpi/open-bibtex-pdf-or-directory () + (interactive) + (save-excursion + (bibtex-beginning-of-entry) + (let* ((bibtex-expand-strings t) + (entry (bibtex-parse-entry t)) + (key (reftex-get-bib-field "=key=" entry)) + (pdf (funcall org-ref-get-pdf-filename-function key))) + (if (file-exists-p pdf) + (org-open-link-from-string (format "[[file:%s]]" pdf)) + (dired (file-name-directory pdf)) + (ding)))))) #+end_src ***** Capturing entries I store my bibtex references in an org file together with my notes. In @@ -3379,86 +4241,9 @@ Here's a function to easily copy a doi from the results of =crossref-lookup=. (let ((doi (alist-get 'doi (cdr (biblio--selection-metadata-at-point))))) (kill-new doi) (message "Copied doi %s" doi))) -(define-key biblio-selection-mode-map "d" #'fpi/biblio-get-doi) -#+end_src -*** Todo settings -- WAITING tasks are waiting on the completion of other tasks -- NEXT tasks can be picked up -- INPROGRESS are current tasks with time clocked -- DONE are complete tasks -- ICEBOX tasks are on ice for whatever reason - -TODO->DONE cycle is for habits.\\ -Idle states cover things to do for time in between, checking the -inbox, reading news, … - -Phonecalls? - -#+BEGIN_SRC dot :file /tmp/todo.png -digraph hierarch{ - node [shape=box] - // Tasks, Projects - PLANNING -> NEXT, INPROGRESS, ICEBOX - WAITING -> NEXT -> INPROGRESS -> DONE, WAITING, ICEBOX - NEXT -> WAITING -> INPROGRESS, ICEBOX - NEXT -> ICEBOX, DONE - - // stuff for idle time - IDLE -> IDLE - //NEXT -> DONE - - // Phonecalls, Meetings - PHONE -> DONE, CANCELED - MEETING -> DONE, CANCELED -} -#+END_SRC - -#+RESULTS: -[[file:/tmp/todo.png]] - -#+BEGIN_SRC emacs-lisp :tangle no -(setq org-todo-keywords '((sequence "PLANNING(p)" "NEXT(n)" "INPROGRESS(i)" "WAITING(w@/!)" "|" "ICEBOX(x@)" "DONE(d)") - (sequence "S(s)" "DONE(d)") - (sequence "PHONE(P)" "MEETING(m)" "|" "CANCELLED(c)") - (sequence "TODO(t)" "|" "DONE(d)") - (sequence "IDLE(a)"))) -(setq org-use-fast-todo-selection t) - - -(setq org-todo-keyword-faces - '(("NEXT" :foreground "light blue" :weight bold) - ("INPROGRESS" :foreground "burlywood" :weight bold) - ("DONE" :foreground "forest green" :weight bold) - ("WAITING" :foreground "orange" :weight bold) - ("ICEBOX" :foreground "orange" :weight normal) - ("CANCELLED" :foreground "forest green" :weight bold) - ("MEETING" :foreground "yellow" :weight bold) - ("PHONE" :foreground "yellow" :weight bold) - ("IDLE" :foreground "magenta" :weight bold))) -#+END_SRC - -Switch a todo entry from NEXT to INPROGRESS when clocking in. -#+begin_src emacs-lisp :tangle no -(setq org-clock-in-switch-to-state 'bh/clock-in-to-inprogress) -(defun bh/clock-in-to-inprogress (kw) - "Switch a task from NEXT to INPROGRESS when clocking in. -Skips capture tasks, projects, and subprojects. -Switch projects and subprojects from NEXT back to TODO" - (when (not (and (boundp 'org-capture-mode) org-capture-mode)) - (cond - ((and (member (org-get-todo-state) (list "NEXT")) - (bh/is-task-p)) - "INPROGRESS") - ((and (member (org-get-todo-state) (list "NEXT")) - (bh/is-project-p)) - "INPROGRESS")))) -#+end_src -**** State changes -Track state changes to done & changes to schedules and deadlines. -#+begin_src emacs-lisp :tangle no -(setq org-log-done 'time) -(setq org-log-redeadline 'time) -(setq org-log-reschedule 'time) +(use-package biblio + :bind (:map biblio-selection-mode-map + ("d" . fpi/biblio-get-doi))) #+end_src *** Toggle drawer visibility #+begin_src emacs-lisp @@ -3497,6 +4282,12 @@ Track state changes to done & changes to schedules and deadlines. (mw-org-hide-meta-heading-info))) (define-key fpi/toggle-map "m" #'fpi/org-toggle-meta-info-lines) #+end_src +*** Table of contents in org +#+begin_src emacs-lisp +(use-package toc-org + :straight t + :hook (org-mode . toc-org-mode)) +#+end_src *** Workflow My current workflow is largely inspired by [[http://doc.rix.si/cce/cce-org.html][Ryan Rix's]] and [[http://doc.norang.ca/org-mode.html][Bernt Hansen's]] configs. @@ -4287,16 +5078,6 @@ For now I use this bad code. (lambda () (define-key gnus-summary-mode-map (kbd ";") 'bbdb-mua-edit-field))) #+end_src -** Spellcheck -#+begin_src emacs-lisp -(use-package ispell - :config - (setq ispell-program-name "/usr/bin/hunspell") - (setq ispell-dictionary "en_US,de_DE") - (ispell-set-spellchecker-params) - (ispell-hunspell-add-multi-dic "en_US,de_DE") - ) -#+end_src ** Compile Fix ansi colors in compile buffers. From [[https://endlessparentheses.com/ansi-colors-in-the-compilation-buffer-output.html][endlessparentheses]]. #+begin_src emacs-lisp @@ -4522,6 +5303,28 @@ End sentences with single spaces. #+begin_src emacs-lisp (setq sentence-end-double-space nil) #+end_src +** Spellcheck +#+begin_src emacs-lisp +(use-package ispell + :config + (setq ispell-program-name "/usr/bin/hunspell") + (setq ispell-dictionary "en_US,de_DE") + (ispell-set-spellchecker-params) + (ispell-hunspell-add-multi-dic "en_US,de_DE") + ) +#+end_src +*** Flyspell +Setup mainly from [[https://github.com/howardabrams/dot-files/blob/master/emacs.org][Howard Abrams]]. +#+begin_src emacs-lisp +(use-package flyspell + :delight + :init + (add-hook 'prog-mode-hook 'flyspell-prog-mode) + (dolist (hook '(text-mode-hook org-mode-hook)) + (add-hook hook (lambda () (flyspell-mode 1)))) + (dolist (hook '(change-log-mode-hook log-edit-mode-hook org-agenda-mode-hook)) + (add-hook hook (lambda () (flyspell-mode -1))))) +#+end_src * Interface ** General #+begin_src emacs-lisp @@ -4613,6 +5416,55 @@ temporary buffer is created. (electric-pair-mode 1) (electric-quote-mode -1)) #+end_src +** Writing Setup +I gather all settings related to writing in a minor mode. +#+begin_src emacs-lisp +(define-minor-mode prose-mode + "Toggle some settings for text based buffers." + :init-value nil + :lighter " ✎" + (if prose-mode + (progn + (olivetti-mode 1) + (set-window-fringes (selected-window) 0 0) + (variable-pitch-mode 1) + ) + (olivetti-mode -1) + (set-window-fringes (selected-window) nil) + (variable-pitch-mode -1) + )) +#+end_src +The mode is enabled for all =text-mode= based buffers by default. +#+begin_src emacs-lisp +(add-hook 'text-mode-hook 'prose-mode) +#+end_src +Also set an easy keybinding to toggle it manually. +#+begin_src emacs-lisp :noweb-ref fpi-bindings :tangle no +(define-key fpi/toggle-map "p" #'prose-mode) +#+end_src + +Olivetti mode is used to center text in the buffer. This somehow helps with writing. +#+begin_src emacs-lisp +(use-package olivetti + :straight t + :delight + :custom + (olivetti-body-width 80) + ;; (olivetti-minimum-body-width 70) + ) +#+end_src + +For org-mode also reduce indentation by =org-indent-mode= as described [[https://explog.in/notes/writingsetup.html][here]]. +#+begin_src emacs-lisp :noweb-ref org-indent-custom :tangle no +(org-indent-indentation-per-level 1) +#+end_src +These settings are also from the above blog post, but mainly manually set what =org-indent-mode= does anyway. +#+begin_src emacs-lisp :noweb-ref org-custom :tangle no +(org-adapt-indentation nil) +(org-hide-leading-stars t) +(org-hide-emphasis-markers t) +(org-cycle-separator-lines 1) +#+end_src * Wrapping up Some stuff that is run after everything else. |