diff options
| -rw-r--r-- | .dir-locals.el | 7 | ||||
| -rw-r--r-- | emacs-init.org | 1525 | 
2 files changed, 1215 insertions, 317 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..d2492e2 100644 --- a/emacs-init.org +++ b/emacs-init.org @@ -1,4 +1,66 @@  #+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]] +  - [[#minor-utilities][Minor utilities]] +- [[#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 +92,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 +109,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 +172,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 +316,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 +327,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 +385,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 +614,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 +632,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 +1038,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 +1079,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 +1088,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 +1106,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 +1175,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 +1187,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 +1325,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 +1352,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 +1372,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 +1413,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 +1685,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 +1775,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 +2227,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 +2358,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 +2798,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 +3037,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 +3158,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 +3191,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 +3250,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 +3281,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 +3296,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 +3323,278 @@ 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)) +                      (:title "├Reading" :match "+work+read" :budget "0" :blocks (day week)) +                      (:title "╰Shaved Yaks" :match "+work+nonprod" :budget "0" :blocks (day week)) +                      (:title "Personal" :match "+nowork-nonprod" :budget "5: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 +3610,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 +3695,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,39 +3714,37 @@ 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)~. +*** 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-file-buffer-list () -  "Return a list of org buffers which visit files." -  (seq-filter 'buffer-file-name (org-buffer-list))) +(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 - -#+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. +*** Durations  #+begin_src emacs-lisp -(use-package org-time-budgets -  :straight (:host github :repo "fpiper/org-time-budgets" -                   :branch "develop") +(use-package org-duration +  :after org    :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)))) +  (org-duration-format '(("h" . t) ("min" . t) (special . h:mm))))  #+end_src -*** Column view +*** ox-reveal +#+BEGIN_SRC emacs-lisp +(use-package ox-reveal +  :straight t) +(use-package reveal) +(setq org-reveal-root (concat "file:///home/fpi/" "reveal.js")) +;;(setq org-reveal-root "http://cdn.jsdelivr.net/reveal.js/3.0.0/") +#+END_SRC + +*** ol-bbdb  #+begin_src emacs-lisp -(setq org-columns-default-format -      "%50ITEM(Task) %5Effort(Effort){:} %5CLOCKSUM %3PRIORITY %20DEADLINE %20SCHEDULED %20TIMESTAMP %TODO %CATEGORY %TAGS") +(use-package ol-bbdb)  #+end_src -*** org-caldav +*** icalendar support +While =org-caldav= offers syncing with caldav servers it relies on =ox-icalendar= to convert between org entries and icalendar events. +**** org-caldav  #+begin_src emacs-lisp  (use-package org-caldav    :straight t @@ -2856,61 +3755,15 @@ 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 +**** ox-icalendar  #+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 -*** ox-reveal -#+BEGIN_SRC emacs-lisp -(use-package ox-reveal -  :straight t) -(use-package reveal) -(setq org-reveal-root (concat "file:///home/fpi/" "reveal.js")) -;;(setq org-reveal-root "http://cdn.jsdelivr.net/reveal.js/3.0.0/") -#+END_SRC - -*** ol-bbdb -#+begin_src emacs-lisp -(use-package ol-bbdb) +(use-package ox-icalendar +  :after org +  :custom +  (org-icalendar-store-UID t))  #+end_src  *** prettify symbols  Set some prettify symbols for org mode. @@ -2961,17 +3814,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 +3902,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 +3913,47 @@ 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 + "** %a +:PROPERTIES: +:CREATED: %U +:END:  %i%?" ) -("je" "Journal Entry" +("je" "Manual Entry"   entry   (file+olp+datetree    ,org-journal-file) - "** %<%H:%M> %? + "** %? +:PROPERTIES: +:CREATED: %U +:END:  %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) + "** %(org-web-tools--org-link-for-url (org-web-tools--get-first-url))%? +:PROPERTIES: +:CREATED: %U +:END: +%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 +4079,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 +4196,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 +4263,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 +4304,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. @@ -3842,7 +4655,7 @@ To open and hide a shell quickly I use =shell-pop=.  #+begin_src emacs-lisp  (use-package shell-pop    :straight t -  :bind (("C-!" . shell-pop)) +  :bind (("C->" . shell-pop))    :custom    (shell-pop-shell-type (quote ("eshell" "*eshell*" (lambda nil (eshell))))))  #+end_src @@ -4192,7 +5005,7 @@ Hard new lines are identified using a ~hard~ text property and  displayed as =⏎=. We need to make sure all newlines inserted by  message initialization (signature, ...) also have this text property.  For now I use this bad code. -#+BEGIN_SRC emacs-lisp +#+BEGIN_SRC emacs-lisp :tangle no  (use-package messages-are-flowing    :straight t    :config (add-hook 'message-mode-hook 'messages-are-flowing-use-and-mark-hard-newlines)) @@ -4287,16 +5100,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 @@ -4517,11 +5320,50 @@ _q_ quit       _r_ remove result  _e_ examplify region    ("/" ibuffer-filter-disable "disable")    ("b" hydra-ibuffer-main/body "back" :color blue))  #+END_SRC +** Minor utilities +*** Screenshots / -casts +[[https://gitlab.com/ambrevar/emacs-gif-screencast][Here]] is a guide to creating emacs gif screencasts. + +If compiled with =cairo= support emacs can directly create svg screenshots ([[https://www.reddit.com/r/emacs/comments/idz35e/emacs_27_can_take_svg_screenshots_of_itself/][source]]). +#+begin_src emacs-lisp +(defun screenshot-svg () +  "Save a screenshot of the current frame as an SVG image. +Saves to a temp file and puts the filename in the kill ring." +  (interactive) +  (let* ((filename (make-temp-file "Emacs-" nil ".svg")) +         (data (x-export-frames nil 'svg))) +    (with-temp-file filename +      (insert data)) +    (kill-new filename) +    (message filename))) +#+end_src  * Language settings  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 +5455,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.  | 
