summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.dir-locals.el7
-rw-r--r--emacs-init.org1525
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.