Skip to content

Files

Latest commit

eda3c0a · Apr 11, 2025

History

History
17072 lines (14032 loc) · 544 KB

org-init.org

File metadata and controls

17072 lines (14032 loc) · 544 KB

Literate Emacs configuration

Table of contents

Good examples

General settings

General settings concerning startup, special variables, package management etc. are stored in an extra file. init.el

General appearence

Toolbar

Don’t show the toolbar:

(tool-bar-mode 0)

Start-up

dashboard

https://github.com/emacs-dashboard/emacs-dashboard

An extensible Emacs startup screen.

(use-package dashboard
	:ensure t
	:diminish dashboard-mode
	:config
	(setq
	 dashboard-banner-logo-title (emacs-version)
	 dashboard-startup-banner 'logo
	 dashboard-items '((recents  . 10)
										 (bookmarks . 10)
										 (projects . 5)
										 )
	 )

	(add-to-list 'dashboard-item-generators  '(custom-settings . dashboard-insert-custom-settings))
	(add-to-list 'dashboard-items '(custom-settings) t)

	(define-key dashboard-mode-map (kbd "<up>") (lambda () (interactive)(widget-forward -1)))
	(define-key dashboard-mode-map (kbd "<down>") (lambda () (interactive)(widget-forward 1)))

	;; (setq dashboard-set-navigator t)
	;; (setq dashboard-navigator-buttons
	;; 			`(;; line1
	;; 				((,(all-the-icons-octicon "mark-github" :height 1.1 :v-adjust 0.0)
	;; 					"Homepage"
	;; 					"Browse homepage"
	;; 					(lambda (&rest _) (browse-url "homepage")))
	;; 				 ("★" "Star" "Show stars" (lambda (&rest _) (show-stars)) warning)
	;; 				 ("?" "" "?/h" #'show-help nil "<" ">"))
	;; 				;; line 2
	;; 				((,(all-the-icons-faicon "linkedin" :height 1.1 :v-adjust 0.0)
	;; 					"Linkedin"
	;; 					""
	;; 					(lambda (&rest _) (browse-url "homepage")))
	;; 				 ("⚑" nil "Show flags" (lambda (&rest _) (message "flag")) error))))

	(dashboard-setup-startup-hook)
	)

Custom settings (https://www.gnu.org/software/emacs/manual/html_mono/widget.html):

(defun dashboard-insert-custom-settings (&rest ignore)
	(interactive)
  (widget-insert "Custom settings:\n\t\t")
	(widget-create
	 'checkbox
	 :notify (lambda (&rest ignore)
						 (if (bound-and-true-p cua-mode)
								 (progn (cua-mode nil)
												(customize-save-variable 'cua-mode nil))
							 (cua-mode t)
							 (customize-save-variable 'cua-mode t)
							 ))
	 (bound-and-true-p cua-mode))
	(widget-insert " Use CUA-mode?"))

Show dashboard when also starting a client:

(setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))

Signals

No beeping:

(setq visible-bell nil)

Syntax highlighting

Show matching brackets:

(show-paren-mode 1)
(setq show-paren-delay 0)

Apply syntax highlighting to all buffers:

(global-font-lock-mode t)

highlight-symbol

https://github.com/nschum/highlight-symbol.el

Quickly highlight a symbol – most likely the word under point – throughout the buffer and cycle through its locations. There is a hydra attached to it: hydra-highlight-symbol

(use-package highlight-symbol
  :ensure t
  :config
  (setq highlight-symbol-idle-delay 0.2)
  (add-hook 'highlight-symbol-mode-hook
            (function
             (lambda () (highlight-symbol-nav-mode +1)))))

Lines

Highlight line of cursor:

(global-hl-line-mode t)

Soft-wrap lines:

(global-visual-line-mode t)

Display line numbers in the modeline:

(line-number-mode t)

Line numbers in the margin

Before Emacs v26, the (sometimes slow) linum package was used for displaying the line numbers in the margin of the window:

;; ;; before Emacs 26
;; (global-linum-mode t)
;; (setq linum-format " %3d ")

With Emcas v26, the display of line numbers is built-in and much faster:

;; ;; with Emacs 26
;; (global-display-line-numbers-mode)

Line numbering can be turned on/off with M-x display-line-numbers-mode.

Relative line numbers will be displayed after setting display-line-numbers as follows:

(setq display-line-numbers 'relative)

Cursor

Let the cursor blink forever:

(blink-cursor-mode 1)										; blink
(setq blink-cursor-blinks 0)						; blink forever

Stretch cursor:

(setq  x-stretch-cursor t)

Text faces

A face is a collection of graphical attributes for displaying text: font, foreground color, background color, optional underlining, etc.

https://www.gnu.org/software/emacs/manual/html_node/emacs/Faces.html

Themes

A theme is a specified collection of faces, so that different types of text is displayed in a different way.

Add local themes directory to search space (just in case):

(setq themes-dir
      (expand-file-name "themes" user-emacs-directory))
(add-to-list 'custom-theme-load-path themes-dir)

monokai-theme

https://github.com/oneKelvinSmith/monokai-emacs

The monokai theme is one of my favourites.

(use-package monokai-theme
	:ensure t
	:config
	(load-theme 'monokai t)
	
	;; font size of org-mode headers 
	(setq monokai-height-minus-1 1.0
        monokai-height-plus-1 1.0
        monokai-height-plus-2 1.1
        monokai-height-plus-3 1.25
        monokai-height-plus-4 1.5)
)

Yet I don’t like how regions are highlighted:

(custom-theme-set-faces
 'monokai
 `(region ((t (:inherit highlight :background "#FFB269" :foreground ,monokai-background))))
 )
(enable-theme 'monokai)									; needed since v27

Switch between font styles

Toggle proportional mode when appropriate. Inspired by https://ogbe.net/blog/toggle-serif.html

(defvar font-preserve-default-list nil
  "A list holding the faces that preserve the default family and
  height when TOGGLE-SERIF is used.")
(setq font-preserve-default-list
      '(;; LaTeX markup
        font-latex-math-face
        font-latex-sedate-face
        font-latex-warning-face
        ;; org markup
        org-latex-and-related
        org-meta-line
        org-verbatim
        org-block-begin-line
        ;; syntax highlighting using font-lock
        font-lock-builtin-face
        font-lock-comment-delimiter-face
        font-lock-comment-face
        font-lock-constant-face
        font-lock-doc-face
        font-lock-function-name-face
        font-lock-keyword-face
        font-lock-negation-char-face
        font-lock-preprocessor-face
        font-lock-regexp-grouping-backslash
        font-lock-regexp-grouping-construct
        font-lock-string-face
        font-lock-type-face
        font-lock-variable-name-face
        font-lock-warning-face))
(defun toggle-proportional ()
  "Change the default face of the current buffer to use a proportional family."
  (interactive)
  (when (display-graphic-p)  ;; this is only for graphical emacs
    ;; the serif font familiy and height, save the default attributes
    (let ((proportional-fam "Segoe UI")
          (proportional-height 125)
          (default-fam (face-attribute 'default :family))
          (default-height (face-attribute 'default :height)))
      (if (not (bound-and-true-p default-cookie))
          (progn (make-local-variable 'default-cookie)
                 (make-local-variable 'preserve-default-cookies-list)
                 (setq preserve-default-cookies-list nil)
                 ;; remap default face to serif
                 (setq default-cookie
                       (face-remap-add-relative
                        'default :family proportional-fam :height proportional-height))
                 ;; keep previously defined monospace fonts the same
                 (dolist (face font-preserve-default-list)
                   (add-to-list 'preserve-default-cookies-list
                                (face-remap-add-relative
                                 face :family default-fam :height default-height)))
                 (message "Turned on proportional font."))
        ;; undo changes
        (progn (face-remap-remove-relative default-cookie)
               (dolist (cookie preserve-default-cookies-list)
                 (face-remap-remove-relative cookie))
               (setq default-cookie nil)
               (setq preserve-default-cookies-list nil)
               (message "Restored default fonts."))))))

UTF8 support

Replace LaTeX commands by UTF8 symbols:

;; (use-package latex-pretty-symbols
;; 	:ensure t)

Emojis

As of Emacs v29, displaying and selecting emojis is now built-in via input methods.

(global-set-key (kbd "C-c i e") 'emoji-search)

Mode line and window labels

Show file path in window title:

(setq frame-title-format
      '(buffer-file-name "%b - %f" ; File buffer
        (dired-directory dired-directory ; Dired buffer
         (revert-buffer-function "%b" ; Buffer Menu
																 ("%b - Dir: " default-directory))))) ; Plain buffer

Show date and time:

(setq display-time-24hr-format t)
(display-time-mode +1)

Fringe style:

;; (set-face-attribute 'fringe nil :background "#3F3F3F" :foreground "#3F3F3F")

smart-mode-line

https://github.com/Malabarba/smart-mode-line/

Make the mode line nicer and better readable.

(use-package smart-mode-line
	:ensure t
	:init
	;; (setq sml/theme 'dark)
	(setq sml/no-confirm-load-theme t)
	:config
	(sml/setup))

Distraction-free mode

writeroom-mode

https://github.com/joostkremers/writeroom-mode

A minor mode for Emacs that implements a distraction-free writing mode similar to the famous Writeroom editor for OS X.

(use-package writeroom-mode
	:ensure t
	:bind
	(:map writeroom-mode-map
				("C-M-<" . writeroom-decrease-width)
				("C-M->" . writeroom-increase-width)
				("C-M-=" . writeroom-adjust-width)
				("C-<f10>" . writeroom-toggle-mode-line)
				)
	)
(global-set-key (kbd "<f10>") 'writeroom-mode)

Overlays

ov

https://github.com/emacsorphanage/ov

Package for manipulating overlays. However, it does not affect font-lock or text-properties.

(use-package ov
  :ensure t)

Icons

all-the-icons

https://github.com/domtronn/all-the-icons.el

all-the-icons makes popular icons available in Emacs.

(use-package all-the-icons
	:ensure t
  :if (display-graphic-p))

Missing fonts can be installed with M-x all-the-icons-install-fonts.

Debugging

font-lock-studio

https://github.com/Lindydancer/font-lock-studio

Interactive debugger for font-lock keywords.

(use-package font-lock-studio
	:ensure t)

Echo area / Messages

https://www.gnu.org/software/emacs/manual/html_node/elisp/The-Echo-Area.html

The echo area is used for displaying error messages, messages created with the message function, and for echoing keystrokes. It is not to be confused with the minibuffer, although it is shown in the same place of an Emacs window,

Messages from the echo area are also recorded in the *Messages* buffer.

Format of *Messages* buffer

The following code adds timestamps to messages in the *Messages* buffer. It is a modified version of https://emacs.stackexchange.com/a/64551/12336.

(defvar last-message-with-timestamp nil
  "Last message with timestamp appended to it.")

(defun add-timestamp-to-message (format-string &rest args)
  "Prepend timestamp to each message in message buffer.

FORMAT-STRING and ARGS are used by `message' to print a formatted string.

Enable with (advice-add 'message :before 'add-timestamp-to-message)"
  (when (and message-log-max
             (not (string-equal format-string "%s%s")))
    (let ((formatted-message-string (if args
                                        (apply 'format `(,format-string ,@args))
                                      format-string)))
      (unless (string= formatted-message-string last-message-with-timestamp)
        (setq last-message-with-timestamp formatted-message-string)
        (let ((deactivate-mark nil)
              (inhibit-read-only t))
          (with-current-buffer "*Messages*"
            (goto-char (point-max))
            (when (not (bolp))
              (newline))
            (insert (format-time-string "[%F %T] "))))))))

(advice-add 'message :before 'add-timestamp-to-message)

Minibuffer

General

Shorten yes/no answers to y/n:

(fset 'yes-or-no-p 'y-or-n-p)

ido, ivy

Currently, I’m using neither of the two.

imenu

https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html

Imenu is built into Emacs and helps to navigate between “definitions”, e.g. defun statements in lisp code, within a buffer.

imenu-anywhere

https://github.com/vspinu/imenu-anywhere

Imenu navigation across buffers of the same type (major mode, project).

(use-package imenu-anywhere
	:ensure t)

helm

https://github.com/emacs-helm/helm https://tuhdo.github.io/helm-intro.html

Emacs framework for incremental completions and narrowing selections.

(use-package helm
  :diminish helm-mode
  :init
  (progn
    ;; (require 'helm-config) ; Obsolete since v3.9.1: https://github.com/emacs-helm/helm/commit/e81fbbc687705595ab65ae5cd3bdf93c17a90743
    (setq helm-candidate-number-limit 100)
    (setq helm-idle-delay 0.0 ; update sources immediately
          helm-input-idle-delay 0.01		; update input quickly
          helm-yas-display-key-on-candidate t
          helm-M-x-requires-pattern nil
          helm-ff-skip-boring-files t
					helm-mode-fuzzy-match t 			; global fuzzy match
					helm-buffers-fuzzy-matching t
					helm-recentf-fuzzy-match t
					helm-M-x-fuzzy-match t
          helm-follow-mode-persistent t	; follow candidate in buffer (with C-up/C-down)
					helm-imenu-fuzzy-match t
					helm-completion-in-region-fuzzy-match t
					helm-apropos-fuzzy-match t
					helm-autoresize-mode 1 				; re-size the completion window based on number of candidates
					helm-adaptive-mode t					; show commonly used commands first
					helm-move-to-line-cycle-in-source nil ; if non-nil, cycle only within a source
					)
		(setq bibtex-completion-bibliography user-bibliography-file
					bibtex-completion-library-path user-bibliography-pdf-dir ; directory of PDFs
					bibtex-completion-notes-path user-bibliography-notes-dir ; directory of notes
					)
		
		;; helm-mini
		(setq helm-mini-default-sources
					'(helm-source-buffers-list
						helm-source-bookmarks
						helm-source-recentf
						helm-source-buffer-not-found)) 

    (helm-mode)

		;; ;; http://emacs.stackexchange.com/a/7896/12336
		;; ;; <return> opens directory in helm-find-files, not dired
		;; (defun fu/helm-find-files-navigate-forward (orig-fun &rest args)
		;; 	(if (file-directory-p (helm-get-selection))
		;; 			(apply orig-fun args)
		;; 		(helm-maybe-exit-minibuffer)))
		;; (advice-add 'helm-execute-persistent-action :around #'fu/helm-find-files-navigate-forward)
		;; (define-key helm-find-files-map (kbd "<return>") 'helm-execute-persistent-action)
		
    ;; http://emacs.stackexchange.com/a/7896/12336
		;; <backspace> before backslash lets helm-find-files  move one directory up
		(defun fu/helm-find-files-navigate-back (orig-fun &rest args)
			(if (= (length helm-pattern) (length (helm-find-files-initial-input)))
					(helm-find-files-up-one-level 1)
				(apply orig-fun args)))
		(advice-add 'helm-ff-delete-char-backward :around #'fu/helm-find-files-navigate-back)

		;; ;; https://redd.it/3f55nm
		;; ;; Remove . and .. from helm-find-files
		;; (advice-add 'helm-ff-filter-candidate-one-by-one
		;; 						:around (lambda (fcn file)
		;; 											(unless (string-match "\\(?:/\\|\\`\\)\\.\\{1,2\\}\\'" file)
		;; 												(funcall fcn file))))

		)
  :bind (("M-y" . helm-mini)
				 ("C-x C-r" . helm-recentf)
         ("C-h a" . helm-apropos)
         ("C-x C-b" . helm-buffers-list)
         ("C-x b" . helm-buffers-list)
				 ("C-x C-f" . helm-find-files)
         ("C-x C-y" . helm-show-kill-ring)
         ("C-x y" . helm-show-kill-ring)
         ("C-x t" . helm-etags-select)
				 ("C-x C-t" . helm-etags-select)
         ("C-x SPC" . helm-all-mark-rings)
         ("C-x C-SPC" . helm-all-mark-rings)				 
         ("M-x" . helm-M-x)
         ("C-s" . helm-occur)
         ;; ("C-x c s" . helm-swoop)
         ("C-x c y" . helm-yas-complete)
         ("C-x c Y" . helm-yas-create-snippet-on-region)
         ("C-x c SPC" . helm-all-mark-rings)
				 ("C-ß" . helm-imenu)
				 ("C-S-?" . helm-imenu-anywhere)
				 )
	)
(ido-mode -1)														; turn off ido mode, just in case

helm-descbinds

https://github.com/emacs-helm/helm-descbinds

List active key bindings.

(use-package helm-descbinds
	:ensure t
  :bind ("C-h b" . helm-descbinds))

org-mode

https://github.com/emacs-helm/helm-org

Complete tags with helm when using org-set-tags:

(use-package helm-org
	:ensure t
	:pin MELPA
	:config
	(add-to-list 'helm-completing-read-handlers-alist '(org-capture . helm-org-completing-read-tags))
	(add-to-list 'helm-completing-read-handlers-alist '(org-set-tags . helm-org-completing-read-tags))
	)

helm-swoop

https://github.com/emacsorphanage/helm-swoop

Search buffer while showing matches in a separate buffer.

(use-package helm-swoop
	:ensure t
	:pin MELPA
	:config
	;; Move up and down like isearch
	(define-key helm-swoop-map (kbd "C-r") 'helm-previous-line)
	(define-key helm-swoop-map (kbd "C-s") 'helm-next-line)
	(define-key helm-multi-swoop-map (kbd "C-r") 'helm-previous-line)
	(define-key helm-multi-swoop-map (kbd "C-s") 'helm-next-line)

	;; From helm-swoop to helm-multi-swoop-all
	(define-key helm-swoop-map (kbd "M-i") 'helm-multi-swoop-all-from-helm-swoop)

	;; Instead of helm-multi-swoop-all, you can also use helm-multi-swoop-current-mode
	(define-key helm-swoop-map (kbd "M-m") 'helm-multi-swoop-current-mode-from-helm-swoop)
	
	;; If nil, you can slightly boost invoke speed in exchange for text color
	(setq helm-swoop-speed-or-color t)
	
	;; Optional face for line numbers
	;; Face name is `helm-swoop-line-number-face`
	(setq helm-swoop-use-line-number-face t)

	;; If you prefer fuzzy matching (seems to be already activated)
	;; (setq helm-swoop-use-fuzzy-match t)

	;; Do not call helm-swoop with symbol or word at point
	(setq helm-swoop-pre-input-function
				(lambda () nil))

  :bind ("C-c /" . helm-swoop))

swiper-helm

https://github.com/abo-abo/swiper-helm

Swiper with Helm backend.

Issues:

  • [ ] Error: “swiper-helm: Cannot open load file: No such file or directory, helm-match-plugin”
(use-package swiper-helm
  :ensure t
  :bind ("C-s" . swiper-helm))

helm-dash

https://github.com/dash-docs-el/helm-dash

Browse Dash docsets with helm. helm-dash depends on sqlite3 which you probably have to install manually: http://sqlite.org/download.html

(use-package helm-dash
	:ensure t
	:init
	(setq helm-dash-common-docsets			; active in all buffers
				'())
	(setq helm-dash-browser-func 'eww)		; use internal web browser
	(setq helm-dash-docsets-path docsets-dir) ; FIXME: under windows, helm-dash does not install docsets here but in ~/AppData/... Because of missing tar command?
	(add-hook 'latex-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("LaTeX"))))
	(add-hook 'TeX-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("LaTeX"))))
	(add-hook 'emacs-lisp-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("Emacs Lisp"))))
	;; (add-hook 'js2-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("JavaScript"))))
	(add-hook 'org-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("Org_Mode"))))
	(add-hook 'plantuml-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("PlantUML"))))
	(add-hook 'sh-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("Bash"))))
	;; (add-hook 'perl-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("Perl"))))
	(add-hook 'python-mode-hook (lambda () (interactive)(setq-local helm-dash-docsets '("Python 3" "SciPy" "NumPy"))))
	:bind
	(("C-h d" . helm-dash))
	)

helm-mu

https://github.com/emacs-helm/helm-mu

Helm frontend for Mu and mu4e.

Issues:

  • [X] Does not find emails when using a non-standard mu folder.
(use-package helm-mu
  :ensure t
	:after helm mu4e
	:config
	(setq helm-mu-append-implicit-wildcard t
				helm-mu-default-search-string "(maildir:/INBOX OR maildir:/Sent)"
				helm-mu-contacts-personal t	 ; Only show contacts who sent you emails directly
				helm-mu-command-arguments (concat "--muhome=" mu4e-mu-home) ; Search in `mu4e-mu-home' when calling `mu'
				;; helm-mu-gnu-sed-program "gsed"
				)
	(define-key mu4e-main-mode-map "f" 'helm-mu)
	(define-key mu4e-headers-mode-map "f" 'helm-mu))

helm-org-rifle

https://github.com/alphapapa/org-rifle

Quick, comprehensive search on org-mode files.

(use-package helm-org-rifle
	:ensure t
	:pin MELPA
  :config
	(define-key helm-org-rifle-map (kbd "<left>") 'backward-char) ; instead of helm-previous-source
	(define-key helm-org-rifle-map (kbd "<right>") 'forward-char) ; instead of helm-next-source
	(define-key helm-org-rifle-map (kbd "C-n") 'helm-next-source)
	(define-key helm-org-rifle-map (kbd "C-p") 'helm-previous-source)
	(setq helm-org-rifle-show-path t)
	)

helm-ag

https://github.com/emacsorphanage/helm-ag

Helm interface to The Silver Searcher.

(use-package helm-ag
	:ensure    t
	;; :config
	;; (setq helm-ag-base-command "rg  --vimgrep --no-heading --smart-case") ; use ripgrep instead of ag
	)

helm-cider

https://github.com/clojure-emacs/helm-cider

Helm interface to CIDER.

(use-package helm-cider
	:ensure t
	:hook ((cider-mode . helm-cider-mode)
				 (clojure-mode . helm-cider-mode)))

helm-recoll

https://github.com/emacs-helm/helm-recoll

Helm interface to recoll.

(use-package helm-recoll
	:ensure t
  :commands helm-recoll
  :init (setq helm-recoll-directories
              '(("all" . "~/.recoll"))))

Keys

General settings

;; M-x in minibuffer quits the minibuffer
(add-hook 'minibuffer-setup-hook
					(lambda ()
						(local-set-key (kbd "M-x") 'abort-recursive-edit)))

;; M-y in minibuffer quits the minibuffer
(add-hook 'minibuffer-setup-hook
					(lambda ()
						(local-set-key (kbd "M-y") 'abort-recursive-edit)))

;; C-ß in minibuffer quits the minibuffer
(add-hook 'minibuffer-setup-hook
					(lambda ()
						(local-set-key (kbd "C-ß") 'abort-recursive-edit)))

;; C-s in minibuffer quits the minibuffer
(add-hook 'minibuffer-setup-hook
					(lambda ()
						(local-set-key (kbd "C-s") 'abort-recursive-edit)))

;; (global-set-key (kbd "C-x C-b") 'switch-to-buffer) ; instead of 'list-buffers (see helm)
;; (global-set-key (kbd "C-x b") 'ibuffer)
(global-set-key (kbd "C-x C-k") 'kill-buffer)  

which-key

https://github.com/justbur/emacs-which-key

Show possible completions of entered key sequences.

(use-package which-key
	:ensure t
	:config
	(which-key-mode))

Major modes

org-mode

https://orgmode.org/

Major mode for writing notes and much much more.

Must appear before LaTeX stuff!

Documentation and examples:

Historical remark on taking notes:

My GTD setup in org-mode

Other good examples:

Directories & files

  • ~/org/attachements/
  • ~/org/calendar/
  • ~/org/captures.org
  • ~/org/contacts.org
  • ~/org/elfeed.org
  • ~/org/home/home.org
  • ~/org/journal.org
  • ~/org/work/work.org
  • ~/org/zettel/

General structure of files

Heading typePropertiesBodyPurposeEnd of lifeTitle format
areanon-intersecting with other areastopics, projects, …group topics, keep overview
topic:topic:topics, projects, …group content, keeping overview
projectSCHEDULED, DEADLINE, outcome, :project:, DIRtodos, notes, events that lead to some common outcometrack progress:ARCHIVE:$outcome: $topic
todoSCHEDULED, DEADLINEtext, todo, eventtrack progress, take action:ARCHIVE:
eventtimestamp, location, participants, :event:minutes/notesstore observations, ideas, action items:ARCHIVE:$topic/$participants $timestamp
noteCREATED, LAST_CHANGED, :media:, …textstore ideas, concepts, references, …

Areas (e.g. administration, research, teaching) are usually implemented as separate org-mode files with the following general structure:

  • Clocking (for general clocking)
  • Inbox
    • Events
    • Projects
    • Notes
  • Topics
    • Events
    • Projects
    • Notes
  • Archive
    • Topics

Information flow:

Structure of files for teaching

The area file for teaching is organized similar to the format shown above:

  • Clocking (for general clocking)
  • Inbox
  • Courses & course ideas
    • Courses are treated as projects – see below
  • Notes about teaching methods and goals
    • Typsetting
    • How to give talks
    • etc.
  • Supervised theses
  • Administration
    • Accreditation
    • etc.
  • Archive

Courses have the following specific headings:

Heading typePropertiesTitle formatBody
coursesemester, :project:, DIR(Semester) Title of courselist of sessions
sessiontimestamp, :event:Nth session Shorttitle of course <timestamp>list of teaching units
teaching unitpresenter, literature, material, attachmentsTitle of teaching unitnotes

Here is an example of how they might be structured:

* (Semester) Course name with abbreviation XYZ
:PROPERTIES:
:DIR: ~/path/to/course/folder
:END:

** Sessions

*** 1. session XYZ <active timestamp>

**** Topic of teaching unit

** Term papers / projects

** Homework / exercises

** Course description

** Topics & notes

Every course is linked with a directory (via DIR property) including the following subdirectories depending on the type of event:

  • slides (by me)
  • presentations (by students)
  • term papers (by students)
  • literature
  • exercises (instructions & submissions)

Structure of notes on texts, videos etc. in one big file

Notes regarding texts, videos etc. are handled in the following way:

  • one heading per piece
  • tags: media > text, video, audio, live
  • properties: ID
  • body: URL or org-ref citation link
  • When available, an electronic copy (usually as PDF) is stored as regular attachment based on the heading’s ID. As far as PDFs are concerned, notes can be left there with pdf-tools.
  • eternal

I use org-roam to look through my notes on literature.

Projects

  • Projects have a defined start and end, similar to events, but in contrast to notes and topics.
  • Projects have a defined goal, which is achieved via todo items (either a list of checkboxes or headings), in contrast to events (which usually serve a goal but don’t reach the goal unfortunately).

How to deal with and define projects within org-mode:

General appearence

Lists

Set indentation of list items:

(setq-default org-list-indent-offset 4)

Show bullet instead of - or *.

(font-lock-add-keywords
 'org-mode
 '(("^[[:space:]]*\\(-\\) "
		(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

;; ;; The following tries to estimate the embedding level via the number of preceding spaces.
;;
;; (font-lock-add-keywords
;;  'org-mode
;;  '(("^\\(-\\) "
;; 		(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))
;;
;; (font-lock-add-keywords
;;  'org-mode
;;  `((,(concat "^[[:space:]]\\{" (number-to-string (+ 2 org-list-indent-offset)) "\\}\\(-\\) ")
;; 		(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "◦"))))))
;;
;; (font-lock-add-keywords
;;  'org-mode
;;  `((,(concat "^[[:space:]]\\{" (number-to-string 
;; 																(* 2 (+ 2 org-list-indent-offset))) "\\}\\(-\\) ")
;; 		(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "▸"))))))
;;
;; (font-lock-add-keywords
;;  'org-mode
;;  `((,(concat "^[[:space:]]\\{" (number-to-string 
;; 																(* 3 (+ 2 org-list-indent-offset))) "\\}\\(-\\) ")
;; 		(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "▹"))))))

;; ;; Trying to make bullet face aware of embedding depth:
;;
;; (font-lock-add-keywords
;;  'org-mode
;;  '(("^[[:space:]]*\\(-\\) "
;;     (0 (prog1 () (let ((bullet "-")
;; 											 (depth (org-list--depth (org-element-at-point))))
;; 									 (cond ((= depth 2) (setq bullet "•"))
;; 												 ((= depth 3) (setq bullet "▸"))
;; 												 ((= depth 4) (setq bullet "-"))
;; 												 ((= depth 5) (setq bullet "▪"))
;; 												 )
;; 									 (compose-region (match-beginning 1) (match-end 1) bullet)))))))

Allow for alphabetical list item labels:

(setq org-list-allow-alphabetical t)

Color text

Text can be colored using the link syntax, e.g. this is red. Taken from https://github.com/jkitchin/jmax/blob/master/org-colored-text.el See also http://kitchingroup.cheme.cmu.edu/blog/2016/01/16/Colored-text-in-org-mode-with-export-to-HTML/ Depends on ov.

(use-package org-colored-text
	:after ov)

Add support for export to LaTeX:

;; Taken and adapted from org-colored-text
(org-add-link-type
 "color"
 (lambda (path)
   "No follow action.")
 (lambda (color description backend)
   (cond
		((eq backend 'latex)									; added by TL
     (format "{\\color{%s}%s}" color description)) ; added by TL
    ((eq backend 'html)
     (let ((rgb (assoc color color-name-rgb-alist))
					 r g b)
       (if rgb
					 (progn
						 (setq r (* 255 (/ (nth 1 rgb) 65535.0))
									 g (* 255 (/ (nth 2 rgb) 65535.0))
									 b (* 255 (/ (nth 3 rgb) 65535.0)))
						 (format "<span style=\"color: rgb(%s,%s,%s)\">%s</span>"
										 (truncate r) (truncate g) (truncate b)
										 (or description color)))
				 (format "No Color RGB for %s" color)))))))
Other link types

Link type for typesetting linguistic examples:

(org-link-set-parameters
 "bsp"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)								
						 (format "\\bsp{%s}" (or desc path)))
						((eq 'html backend)
             (format "<font color=\"blue\">%s</font>"
                     (or desc path)))))
 :face '(:foreground "CornflowerBlue"	:slant italic	:weight bold		)
 :help-echo "This will be exported as example.")

(org-link-set-parameters
 "bspcolor"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)								
						 (format "\\bspcolor{%s}" (or desc path)))
						((eq 'html backend)
             (format "<font color=\"blue\">%s</font>"
                     (or desc path)))))
 :face '(:foreground "CornflowerBlue")
 :help-echo "This will be exported in the color of examples.")

Link type for typesetting terminology:

(org-link-set-parameters
 "term"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)								
						 (format "\\term{%s}" (or desc path)))
						((eq 'html backend)
             (format "<span style=\"font-variant:small-caps;\">%s</span>" 
										 (or desc path)))))
 :face '(:box t :slant normal)
 :help-echo "This will be exported as term."
 )

Link type for typesetting emphasized text:

(org-link-set-parameters
 "emph"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)								
						 (format "\\emph{%s}" (or desc path)))
						((eq 'html backend)
             (format "<em>%s</em>"
                     (or desc path)))))
 :face '(:overline t :underline t :slant italic)
 :help-echo "This will be exported as emphasized text."
 )

Link type for typesetting small caps:

(org-link-set-parameters
 "textsc"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)
						 (if (not (string= path ""))
								 (format "{\\color{%s}\\textsc{%s}}" path desc)
							 (format "\\textsc{%s}" desc)))
						((eq 'html backend)
						 (format "<span style=\"font-variant:small-caps;\">%s</span>" 
										 (or desc path)))))
 :face (lambda (path)
				 `(:box t :slant normal  
								:foreground ,(if (not (string= "" path)) "CornflowerBlue")))
 :help-echo "This will be exported in small caps.")

Link type for typesetting with typewriter font:

(org-link-set-parameters
 "texttt"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)
						 (if (not (string= path ""))
								 (format "{\\color{%s}\\texttt{%s}}" path desc)
							 (format "\\texttt{%s}" desc)))
						((eq 'html backend)
						 (format "<span style=\"font-family:monospace;\">%s</span>" 
										 (or desc path)))))
 :face (lambda (path)
				 `(:box t :slant normal :family ,custom-fixed-pitch-font  
								:foreground ,(if (not (string= "" path)) "CornflowerBlue")))
 :help-echo "This will be exported with typewriter font.")

Link type for typesetting with alert font:

(org-link-set-parameters
 "alert"
 :follow (lambda (path) (message "You clicked me."))
 :export (lambda (path desc backend)
           (cond
            ((eq backend 'latex)
						 (if (not (string= path ""))
								 (format "\\alert<%s>{%s}" path desc)
							 (format "\\alert<.>{%s}" desc)))
						((eq 'html backend)
						 (format "<span style=\"color: red;\">%s</span>" 
										 (or desc path)))))
 :face (lambda (path)
				 '(:box t :slant normal  
								:foreground "red"))
 :help-echo "This will be exported as alerted text.")

Modifying some predefined text faces

(add-hook 'org-mode-hook (lambda ()
                           (variable-pitch-mode t)
                           ;; (text-scale-increase 0.5)
                           ))

(with-eval-after-load 'org
  (set-face-attribute 'org-table nil :inherit 'fixed-pitch)
  (set-face-attribute 'org-verbatim nil :inherit 'fixed-pitch)
  ;; Use the same color for LaTeX code as is used for math code in LaTeX mode.
  (set-face-attribute 'org-latex-and-related nil :foreground "#AE81FF" :inherit 'fixed-pitch)
  ;; ;; The following reuses the color of font-latex-math-face directly. 
	;; ;; Unfortunately, font-latex-math-face may not yet be available when setting the face.   
  ;; (with-eval-after-load 'auctex
  ;;   (set-face-attribute 'org-latex-and-related nil :foreground (face-attribute 'font-latex-math-face :foreground) :inherit 'fixed-pitch))
  (set-face-attribute 'org-link nil :inherit 'fixed-pitch)
  (set-face-attribute 'org-date nil :inherit 'fixed-pitch)
  (set-face-attribute 'org-tag nil :inherit 'fixed-pitch)
  )

Code blocks

(custom-set-faces
 '(org-block ((t (:inherit fixed-pitch )))) ; org-mode >9
 '(org-block-background ((t (:inherit fixed-pitch))))
 '(org-block-begin-line ((t (:underline t))))
 '(org-block-end-line  ((t (:overline t))))
 )

Headings

(custom-set-faces
 '(org-level-1 ((t (:inherit outline-1 :overline t :weight semi-bold ))))
 '(org-level-2 ((t (:inherit outline-2 :overline t :weight semi-bold ))))
 '(org-level-3 ((t (:inherit outline-3 :weight semi-bold ))))
 '(org-level-4 ((t (:inherit outline-4 :weight semi-bold ))))
 '(org-level-5 ((t (:inherit outline-5 :weight semi-bold ))))
 )

Emphasis and monospace

https://orgmode.org/manual/Emphasis-and-Monospace.html

Hide markers:

(setq org-hide-emphasis-markers t)

See also org-appear for automatically toggling visibility.

Todo keywords

Modify some faces:

(face-spec-set 'org-todo '((t (:overline t))))
(face-spec-set 'org-done '((t (:overline t))))

Do not change the face of a headline if it is marked DONE.

(setq org-fontify-done-headline nil)

Bullets, folding symbols, HTML and LaTeX symbols

Nicer bullets:

;; (use-package org-bullets
;; 	:ensure t
;; 	:init
;; 	(setq org-bullets-bullet-list
;; 				'("◉" "◎" "○" "●" "►" "♦" "◇"))
;;  (setq inhibit-compacting-font-caches t) ; effects an acceleration under Windows
;; 	:config 
;; 	(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
;; 	)

Folding symbol:

(setq org-ellipsis "")

Replace HTML/LaTeX code by UTF-8 characters (see org-pretty-entities for an enumeration):

(setq org-pretty-entities t)

Editing

Do not edit in invisible areas of the buffer:

(setq-default org-catch-invisible-edits 'show-and-error)

Inline code

Org offers a way to include code inline with src_<language>[<header arguments>]{<body>}, for example src_latex[:tangle no]{\section{Title}}. See https://orgmode.org/manual/Structure-of-Code-Blocks.html.

For better visibility (and more probably due to my ignorance), I once defined my own poor-man’s version of it, namely @@latex:\section{Title}@@.

(font-lock-add-keywords 
 'org-mode
 '(("\\(@@latex:\\)\\(.*?\\)\\(@@\\)"
		(1 font-lock-comment-face)
		(2 '(org-latex-and-related))
		(3 font-lock-comment-face))
	 ))

(font-lock-add-keywords 
 'org-mode
 '(("\\(@@beamer:\\)\\(.*?\\)\\(@@\\)"
		(1 font-lock-comment-face)
		(2 '(org-latex-and-related))
		(3 font-lock-comment-face))
	 ))

(font-lock-add-keywords 
 'org-mode
 '(("\\(@@html:\\)\\(.*?\\)\\(@@\\)"
		(1 font-lock-comment-face)
		(2 '(org-latex-and-related)) 				; FIXME?
		(3 font-lock-comment-face))
	 ))

;; Does not work
(font-lock-add-keywords 
 'org-mode
 '(("\\(#\\+BEAMER_HEADER:\\)\\(.*\\)$"
		(1 font-lock-comment-face)
		(2 '(org-latex-and-related)))
	 ))

Checkboxes

Fontify checkbox items. (inspired by https://fuco1.github.io/2017-05-25-Fontify-done-checkbox-items-in-org-mode.html)

(defface org-checkbox-todo-text
	'((t (;;:inherit org-todo
								 :overline nil
								 :foreground "red"
								 :weight bold)))
	"Face for the text part of an unchecked org-mode checkbox.")

(font-lock-add-keywords
 'org-mode
 `(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]+\\)?\\[\\(?: \\|-\\|\\([0-9]+\\)/\\2\\)\\][ \t]+[^\n]*\\)" 1 'org-checkbox-todo-text prepend))
 'append)
(defface org-checkbox-done-text
	'((t (;;:inherit org-done
								 :overline nil
								 :foreground "forest green"
								 :weight unspecified)))
	"Face for the text part of a checked org-mode checkbox.")

(font-lock-add-keywords
 'org-mode
 `(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]+\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][ \t]+[^\n]*\\)" 1 'org-checkbox-done-text prepend))
 'append)

Sticky headers

https://github.com/alphapapa/org-sticky-header

Show current header in first line of buffer.

(use-package org-sticky-header
	:ensure t
	:config
	(setq org-sticky-header-always-show-header t
				org-sticky-header-prefix nil 		; don't indent sticky header
				org-sticky-header-full-path 'full) ; values: nil, 'full, 'reversed
	(add-hook 'org-mode-hook 'org-sticky-header-mode))

Outline

Function to only unfold current heading and its content:

;; Taken from https://stackoverflow.com/a/28031539/6452961
;; and slightly modified.
(defun org-show-current-heading-tidily ()
  "Show entry under point, keeping other entries closed."
  (interactive) 
	(save-excursion
		(if (save-excursion (end-of-line) (outline-invisible-p))
				(progn (org-show-entry) (show-children))
			(outline-back-to-heading)
			(unless (and (bolp) (org-on-heading-p))
				(org-up-heading-safe)
				(hide-subtree)
				(error "Boundary reached"))
			(org-overview)
			(org-reveal t)
			(org-show-entry)
			(show-children))))

General keys

(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-c l") 'org-store-link)

(with-eval-after-load 'org
	(define-key org-mode-map (kbd "C-<tab>") nil )
	(define-key org-mode-map (kbd "S-<up>") nil )
	(define-key org-mode-map (kbd "S-<down>") nil )
	(define-key org-mode-map (kbd "S-<left>") nil )
	(define-key org-mode-map (kbd "S-<right>") nil )
	(define-key org-mode-map (kbd "C-n") 'org-next-visible-heading )
	(define-key org-mode-map (kbd "C-p") 'org-previous-visible-heading )
	(define-key org-mode-map (kbd "C-S-p") 'org-backward-heading-same-level)
	(define-key org-mode-map (kbd "C-S-n") #'(lambda () (interactive) (org-backward-heading-same-level -1)))
	(define-key org-mode-map (kbd "C-c C-f") 'org-footnote-action )
	(define-key org-mode-map (kbd "C-c C-t") 'org-todo )
	(define-key org-mode-map (kbd "C-c C-x C-b") 'org-tree-to-indirect-buffer )
	(define-key org-mode-map (kbd "C-c C-t") 'org-todo )
	(define-key org-mode-map (kbd "C-c C-<return>") 'org-ctrl-c-ret )
	;; With Org-mode v9.2, the explicit mapping of C-a and C-e becomes necessary.
	;; See http://lists.gnu.org/archive/html/emacs-orgmode/2019-01/msg00253.html
	(define-key org-mode-map (kbd "C-a") 'org-beginning-of-line )
	(define-key org-mode-map (kbd "C-e") 'org-end-of-line )
	)

;; (with-eval-after-load 'org-agenda
;;   (bind-key "i" 'org-agenda-clock-in org-agenda-mode-map))

Miscellaneous settings

Selection

(setq org-support-shift-select t)

Fold headings at startup

(setq org-startup-folded 'show2levels)

Hide blocks at startup

(setq org-hide-block-startup t)

Indent automatically

(add-hook 'org-mode-hook 'org-indent-mode)

Support for inline tasks

(define-key org-mode-map (kbd "C-c C-x C-t") 'org-inlinetask-insert-task)

C-a and C-e behave org-sensitive

(setq org-special-ctrl-a/e t)

Footnotes

(setq org-footnote-define-inline t
			org-footnote-auto-adjust t)

Jump

(setq org-goto-interface 'outline-path-completion
      org-goto-max-level 10)

IDs

(setq org-id-ts-format (concat "%y%m%dT%H%M%S.%N-" user-acronym)
			org-id-method 'ts)

Agenda

https://orgmode.org/manual/Agenda-Views.html

Show all headers with clocks, timestamps and todos in one overview buffer.

(require 'org-agenda)

General appearance

  • State “TODO” from [2024-07-25 Thu 11:28]

Don’t split window when opening agenda:

(setq org-agenda-window-setup "current-window")

Show notifications of agenda events (NOT USED):

(use-package org-alert
	:ensure t)

Sort agenda TODOs with org-agenda-sorting-strategy.

Skip scheduled items and items with deadline when they are DONE:

(setq org-agenda-skip-scheduled-if-done t
			org-agenda-skip-deadline-if-done t)

Change the face of deadline items in agenda:

(setq org-agenda-deadline-faces
			'((1.0 . org-imminent-deadline)
				(0.5 . org-upcoming-deadline)
				(0.0 . org-upcoming-deadline)
				;; (0.0 . org-upcoming-distant-deadline) ; default
				))

Do not make the block agenda more compact:

(setq org-agenda-compact-blocks nil)

Change the separator between blocks in agenda:

(setq org-agenda-block-separator ?\-)

Set the start day of the weekly agenda:

(setq org-agenda-start-on-weekday 1)	; nil --> today, 0 --> Sunday, 1 --> Monday

Agenda remains open in background. No need to recompile it every time you call it:

(setq org-agenda-sticky t)

Do not show tags in agenda, because it takes too much space:

(setq org-agenda-remove-tags t)

Truncate lines:

(add-hook 'org-agenda-finalize-hook
					#'(lambda () (toggle-truncate-lines t)))

Show tags at the right edge of the screen (DEPRECATED):

;; (setq org-agenda-tags-column (- (- (window-total-width) 3)))

;; taken from https://lists.gnu.org/archive/html/emacs-orgmode/2010-12/msg00410.html
(add-hook 'org-finalize-agenda-hook #'place-agenda-tags)
(defun place-agenda-tags ()
  "Put the agenda tags by the right border of the agenda window."
  (setq org-agenda-tags-column (- 4 (window-width)))
  (org-agenda-align-tags))

Add the Emacs diary (which also stores holidays) to org-agenda:

(setq org-agenda-include-diary t) 

Recenter buffer when coming from the agenda:

(add-hook 'org-agenda-after-show-hook 'recenter-top-bottom)

Enable log mode in order to display done and clocked items:

(setq org-agenda-start-with-log-mode t)
(setq org-agenda-log-mode-items '(closed clock))

Change font faces:

  • [ ] org-agenda-date and org-agenda-date-weekend should depend on org-agenda-date-today. Hard coding of colors should be avoided.
(defun tl/org-agenda-style ()
	(set-face-attribute 'org-agenda-clocking nil :inherit 'unspecified)

	;; Make scheduled items more visible
	;; Note that using `eval-after-load' with org instead does not work. 
	(set-face-attribute 'org-scheduled-today nil :foreground
											(face-attribute 'org-scheduled :foreground))
	(set-face-attribute 'org-scheduled-previously nil :foreground
											(face-attribute 'org-scheduled :foreground))

  ;;;; Does not work, because `org-agenda-date-today' does not seem to be available when org-agenda-mode-hook is executed:
	;; 	(set-face-attribute 'org-agenda-date nil :foreground
	;; 											(face-attribute 'org-agenda-date-today :foreground))
	;; 	(set-face-attribute 'org-agenda-date-weekend nil :foreground
	;; 											(face-attribute 'org-agenda-date-today :foreground))
	;; 	(set-face-attribute 'org-agenda-clocking nil :inherit unspecified)
(set-face-attribute 'org-agenda-date nil :foreground "#66d9ef")
(set-face-attribute 'org-agenda-date-weekend nil :foreground "#66d9ef")

	)
(add-hook 'org-agenda-mode-hook #'tl/org-agenda-style)

Do not dim blocked headings (headings with TODO subheadings):

(setq org-agenda-dim-blocked-tasks nil)

Views

Specifying the timespan of org-agenda views:

(defvar tl/org-agenda-timespan
	'((org-agenda-start-on-weekday nil)
		(org-agenda-span 7)
    (org-agenda-start-day "-3d")))

Specifying the sections of org-agenda views:

(defvar tl/org-agenda-structure
	`((agenda "" ,tl/org-agenda-timespan)
		(tags "PRIORITY=\"A\""
					((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
					 (org-agenda-overriding-header "HIGH-PRIORITY TASKS")))
		;; (todo "INPROGRESS"
		;; 			((org-agenda-overriding-header "ONGOING PROJECTS")))
		(tags "project+TODO=\"INPROGRESS\""
					((org-agenda-overriding-header "ONGOING PROJECTS")))
		(tags "project+TODO=\"WAITING\"" 
					((org-agenda-overriding-header "")
					 (org-agenda-block-separator nil)))
		(todo "NEXT" ((org-agenda-overriding-header "NORMAL-PRIORITY TASKS")))
		(todo "TODO" ((org-agenda-overriding-header "")
									(org-agenda-block-separator nil)))
		(todo "WAITING" ((org-agenda-overriding-header "")
										 (org-agenda-block-separator nil)))
		(todo "HOLD" ((org-agenda-overriding-header "")
									(org-agenda-block-separator nil)))
		(todo "SOMEDAY" ((org-agenda-overriding-header "")
										 (org-agenda-block-separator nil)))
		))

Customize the content of views:

(setq org-agenda-custom-commands
			`(("E" "Events with timestamp"
				 ((tags "event"
                ((org-agenda-overriding-header "Events without date")
                 (org-agenda-prefix-format '((tags . " %i %-12:c %?-12t")))
								 (org-agenda-skip-function
									;; Skip if non-nil and proceed search at returned position.
									;; This mimics org-agenda-skip-entry-if for headings with a simple timestamp
									'(lambda ()
										 (org-back-to-heading t)
										 (let* (;; (beg (point))
														(end (save-excursion (org-end-of-subtree t)(point))
																 ;; Alternative: (org-entry-end-position)
																 ))
											 (and (org-entry-get nil "TIMESTAMP")
														end))))
								 ;; The following does it the usual way, but this would exclude also 
								 ;; headings which are scheduled or with a deadline.   
								 ;; '(org-agenda-skip-entry-if 'timestamp)
								 ))
					(tags "event+TIMESTAMP>=\"<now>\""
								((org-agenda-overriding-header "Future events")
								 (org-agenda-sorting-strategy '((tags ts-down)))
								 (org-agenda-prefix-format '((tags . " %i %-12:c %?-12t")))
								 ))
					(tags "event+TIMESTAMP<=\"<now>\""
								((org-agenda-overriding-header "Past events")
								 (org-agenda-sorting-strategy '((tags ts-down)))
								 (org-agenda-prefix-format '((tags . " %i %-12:c %?-12t")))
								 ))))
				("C" "Clocked Headings"
         ((tags "*"
                ((org-agenda-overriding-header "Clocked Headings")
								 (org-agenda-prefix-format '((tags . " %i %-12:c %?-12t")))
								 (org-agenda-skip-function
									;; Skip if non-nil and proceed search at returned position.
									;; This mimics org-agenda-skip-entry-if for headings with a clock
									'(lambda ()
										 (org-back-to-heading t)
										 (let* ((end (or (save-excursion (outline-next-heading) (point))
																		 (save-excursion (org-end-of-subtree t) (point)))
																 ;; Alternative: (org-entry-end-position)
																 ))
											 (and (not (re-search-forward org-clock-line-re end t))
														end))))
								 (org-agenda-sorting-strategy '((tags user-defined-down)))
								 ))))
				("W" "Work-related agenda and tasks"
				 ,tl/org-agenda-structure
				 ((org-agenda-tag-filter-preset '("+@work"))))
				("H" "Home-related agenda and tasks"
				 ,tl/org-agenda-structure
				 ((org-agenda-tag-filter-preset '("+@home"))))
				("A" "Complete list of agenda and tasks"
				 ,tl/org-agenda-structure
				 ;; ((org-agenda-tag-filter-preset '("+@home")))
				 )
				))

Sorting function for clocked headings:

(defun tl/org-agenda-sort-clocks-function (a b)
	"Custom sorting function for agenda items."
	;; With (get-text-property 1 'org-marker a), one can access the underlying heading of the org-ageanda line.
	(let* ((last-clock-a (org-with-point-at (get-text-property 1 'org-marker a)
												 (org-clock-get-last-clock-out-time)))
				 (last-clock-b (org-with-point-at (get-text-property 1 'org-marker b)
												 (org-clock-get-last-clock-out-time))))
    (if (and last-clock-a last-clock-b)
				(cond ((time-less-p last-clock-a last-clock-b) -1)
							((time-less-p last-clock-b last-clock-a) 1)
							(t nil))
      nil
			;; (if last-clock-a t (not last-clock-b))
			)))

(setq org-agenda-cmp-user-defined 'tl/org-agenda-sort-clocks-function)

Function to check if a heading has CLOCK lines:

(defun tl/org-heading-with-clock-p ()
	"If non-nil, heading und point has a CLOCK line."
	(when (org-log-beginning)
		(org-with-point-at (org-log-beginning)
			(let ((log-end (save-excursion (re-search-forward org-clock-drawer-end-re))))
				(re-search-forward org-clock-line-re log-end t)))))

Set up key bindings for each view:

(defun org-agenda-list-work ()
	(interactive)
	(org-agenda nil "W"))

(defun org-agenda-list-home ()
	(interactive)
	(org-agenda nil "H"))

(defun org-agenda-list-complete ()
	(interactive)
	(org-agenda nil "A"))

(defun org-agenda-list-events ()
	(interactive)
	(org-agenda nil "E"))

(defun org-agenda-list-clocks ()
	(interactive)
	(org-agenda nil "C"))

(define-key org-agenda-mode-map (kbd "W") 'org-agenda-list-work)
(define-key org-agenda-mode-map (kbd "H") 'org-agenda-list-home)
(define-key org-agenda-mode-map (kbd "A") 'org-agenda-list-complete)
(define-key org-agenda-mode-map (kbd "E") 'org-agenda-list-events)
(define-key org-agenda-mode-map (kbd "C") 'org-agenda-list-clocks)

Gantt charts

Gantt charts illustrate project time schedules.

elgantt

https://github.com/legalnonsense/elgantt

Creates a Gantt calendar from your org(-agenda) files.

Show clocked time per day

Heavily inspired by https://emacs.stackexchange.com/questions/21380/show-sum-of-efforts-for-a-day-in-org-agenda-day-title

The following code works directly with the agenda buffer. For each day, the clocked times are collected and then added to the day headers.

(defun tl/org-agenda-collect-clocked-minutes (limit)
  "Sum the clocked time of entries up to LIMIT in the agenda buffer."
  (let ((total '(0)))
    (save-excursion
			(while (and limit
									(< (point) limit))
				;; Just read off the clocked times from the agenda table
				(when (member (org-get-at-bol 'type) '("clock"))
					(push 
					 ;; (org-get-at-bol 'duration) ; Strangely, some durations are negative (Bug?).
					 (org-hh:mm-string-to-minutes
						(if (re-search-forward "Clocked:[[:blank:]]*(\\([0-9]+\\:[0-9]+\\))" (line-end-position) t)
								(match-string 1)
							""))
					 total)
					)
				(forward-line)))
		(apply '+ total)))

(defun tl/org-agenda-insert-clocked-time-per-day ()
  "Insert the clocked time for each day inside the agenda buffer."
  (save-excursion
		(let ((col (save-excursion
								 (text-property-search-forward 'type "clock")		 
								 (re-search-forward "Clocked:[[:blank:]]*?(" nil t)
								 (- (current-column) 2)				; --> -2, if re-search was unsuccessful
								 ))
					(pos)
					(total '(0)))
			;; For each day ... 
			(while 
					(and (text-property-search-forward 'org-agenda-date-header t)
							 (org-get-at-bol 'org-agenda-date-header)) ; double check
				(end-of-line)
				(or (< col (current-column))
						(move-to-column col t))
				(let ((day-clocked-minutes (tl/org-agenda-collect-clocked-minutes
																		(next-single-property-change (point) 'day))))
					(insert-and-inherit (concat " ("
																			(org-duration-from-minutes day-clocked-minutes)
																			")"))
					(push day-clocked-minutes total)))
			;; Go back to week and insert total
			(beginning-of-buffer)
			(when (and (text-property-search-forward 'org-agenda-date-header t)
								 (> (forward-line -1) -1))
				(or (< col (current-column))
						(move-to-column col t))
				(insert-and-inherit (concat " ("
																		(org-duration-from-minutes (apply '+ total))
																		")")))
			)))

(add-hook 'org-agenda-finalize-hook 'tl/org-agenda-insert-clocked-time-per-day)

The function describe-text-properties turned out very helpful to find out the specific (hidden) properties of lines of the org agenda.

Remove timestamps from header text

While timestamps in headers are automatically removed when they trigger the appearance of the header in the agenda, this is not the case for clocked items. The following removes the timestamp also in clocked items.

(defun tl/org-agenda-view-remove-timestamps ()
  "Remove timestamps from the agenda view."
  (save-excursion
		(when (org-agenda-check-type nil 'agenda)
			(goto-char (point-min))
			(while (re-search-forward (concat org-ts-regexp-both " ") nil t)
				(replace-match "")))))

(add-hook 'org-agenda-finalize-hook 'tl/org-agenda-view-remove-timestamps)

org-conflict

org-conflict helps to detect and resolve scheduling conflicts.

https://www.mail-archive.com/emacs-orgmode@gnu.org/msg123154.html

org-clock-convenience

https://github.com/dfeich/org-clock-convenience

Convenience functions for clocking workflow.

(use-package org-clock-convenience
  :ensure t
  :bind (:map org-agenda-mode-map
   						("<M-S-up>" . org-clock-convenience-timestamp-up)
   						("<M-S-down>" . org-clock-convenience-timestamp-down)
   						("ö" . org-clock-convenience-fill-gap)
   						("é" . org-clock-convenience-fill-gap-both)))

Keys

Open heading with double-click:

(define-key org-agenda-mode-map [double-mouse-1] 'org-agenda-switch-to)

Archiving

https://orgmode.org/manual/Archiving.html

Archiving helps to “to keep your working files compact and global searches like the construction of agenda views fast.”

There are two standard ways of archiving headings in Org:

  1. Moving a tree to an archive file
  2. Internal archiving using the ARCHIVE tag

Preserves the first heading of the org-mode file (found in https://orgmode.org/worg/org-hacks.html#org58038ec):

(defadvice org-archive-subtree (around my-org-archive-subtree activate)
  (let ((org-archive-location
         (if (save-excursion (org-back-to-heading)
                             (> (org-outline-level) 1))
             (concat (car (split-string org-archive-location "::"))
                     "::* "
                     (car (org-get-outline-path)))
           org-archive-location)))
    ad-do-it))

Add the outline path to the heading text:

(defadvice org-archive-subtree (around prepend-path-to-heading activate)
  "Prepend the outline path to the heading text before archiving."
  (let* ((heading (org-get-heading 'no-tags 'no-todo))
         (path (org-get-outline-path))
         (full-path (concat (mapconcat 'identity path "") "" heading)))
		(org-edit-headline full-path)
		ad-do-it))

Keep inherited tags of archived headings (found in https://orgmode.org/worg/org-hacks.html#org4449edf): Deactivated for now.

(defadvice org-archive-subtree
  (before add-inherited-tags-before-org-archive-subtree activate)
    "add inherited tags before org-archive-subtree"
    (org-set-tags-to (org-get-tags-at)))

Preserves the first heading of the org-mode file and the inherited tags (found in https://orgmode.org/worg/org-hacks.html#org58038ec): Deactivated because org-extract-archive-file is not found.

(defun my-org-inherited-no-file-tags ()
  (let ((tags (org-entry-get nil "ALLTAGS" 'selective))
        (ltags (org-entry-get nil "TAGS")))
    (mapc (lambda (tag)
            (setq tags
                  (replace-regexp-in-string (concat tag ":") "" tags)))
          (append org-file-tags (when ltags (split-string ltags ":" t))))
    (if (string= ":" tags) nil tags)))

(defadvice org-archive-subtree (around my-org-archive-subtree-low-level activate)
  (let ((tags (my-org-inherited-no-file-tags))
        (org-archive-location
         (if (save-excursion (org-back-to-heading)
                             (> (org-outline-level) 1))
             (concat (car (split-string org-archive-location "::"))
                     "::* "
                     (car (org-get-outline-path)))
           org-archive-location)))
    ad-do-it
    (with-current-buffer (find-file-noselect (org-extract-archive-file))
      (save-excursion
        (while (org-up-heading-safe))
        (org-set-tags tags)))))

Attachments

https://orgmode.org/org.html#Attachments

Org headings and files can be associated with directories which contain their “attachments”.

org-attach might not be autoloaded by org-mode.

(require 'org-attach)  

There are two ways to decide the attachment directory:

  • the ID property, from which the relative path is derived
  • the DIR property which explicitely contains the path

I prefer using general inheritance – child nodes inherit the attachment directory of their parent node – and relative paths in the DIR property.

(setq
 org-attach-use-inheritance t	    ; enable inheritance
 org-attach-dir-relative t		    ; enable relative paths in DIR property
 org-attach-preferred-new-method 'dir ; prefered method for attachments (id, dir, ask, nil)
 )

Note that ID and DIR are inherited asymmetrically, since DIR always takes precedence over ID.

  • If a node has both ID and DIR properties, only the DIR property counts.
  • If a node has an ID property, but its parent has a DIR property, then the DIR property of the parent counts.

Handling attachments of xournalpp files:

(defun tl/org-add-or-open-xournal-attachment ()
	"Start `xournalpp' in attachment directory and open notes file, if available.

If no attachment directory can be found, the user is asked to create one."
	(interactive)
	(if (derived-mode-p 'org-mode)
			(progn (when (and (not (org-attach-dir))
												(y-or-n-p "No attachment directory found.  Create one? "))
							 (org-attach-dir t))
						 (if (org-attach-dir)
								 (start-process "" nil
																"xournalpp" (expand-file-name "hand-notes.xopp" (org-attach-dir)))
							 (start-process "" nil "xournalpp")))
		(message "Not in org-mode")))

Note that one can specify the name of attachment directories per file with

# -*- org-attach-id-dir: "my-fancy-dir-name" -*-

or with a property drawer at the very beginning of the file:

:PROPERTIES:
:DIR: my-fancy-dir-name
:END:

Function to jump from attachment directory in dired to heading (https://fuco1.github.io/2023-02-08-Visit-the-org-headline-from-the-attach-dired-buffer.html):

(defun my-org-attach-visit-headline-from-dired ()
  "Go to the headline corresponding to this org-attach directory."
  (interactive)
  (let* ((id-parts (last (split-string default-directory "/" t) 2))
         (id (apply #'concat id-parts)))
    (let ((m (org-id-find id 'marker)))
      (unless m (user-error "Cannot find entry with ID \"%s\"" id))
      (pop-to-buffer (marker-buffer m))
      (goto-char m)
      (move-marker m nil)
      (org-fold-show-context))))

Add some link types (deprecated since v9.3):

;; (add-to-list 'org-link-abbrev-alist '("attachment" . org-attach-expand-link)) ; included in `org-attach` since v9.3
;; (add-to-list 'org-link-abbrev-alist '("att" . org-attach-expand-link)) ; only for downward compatibility and not supported any longer since v9.4

Function to rename ATTACH_DIR (deprecated since v9.3) to DIR, and to remove ATTACH_DIR_INHERIT (deprecated since v9.3):

(defun org-update-attach-properties ()
    "Change properties for Org-Attach."
    (interactive)
    (org-with-point-at 1
      (while (outline-next-heading)
        (let ((DIR (org--property-local-values "ATTACH_DIR" nil)))
          (when DIR
            (org-set-property "DIR" (car DIR))
            (org-delete-property "ATTACH_DIR"))))
      (org-delete-property-globally "ATTACH_DIR_INHERIT")))

Bibliography

citeproc-org

https://github.com/andras-simonyi/citeproc-org

Renders org-mode citations and bibliographies during export in Citation Style Language (CSL) styles using the citeproc-el library.

citeproc-org is used by helm-bibtex.

(use-package citeproc-org
	:ensure t
	;; :config
	;; (citeproc-org-setup)
	)

Capture templates

(setq org-capture-templates
			`(
				;; uses appointment tag
				("a" "Appointment"
				 entry (file+headline 
								(lambda () (expand-file-name "captures.org" org-directory))
								"Appointments")
				 "* %^T %?\t:appointment:\n\n" 
				 :prepend t
				 :jump-to-captured t
				 ;; :before-finalize org-id-get-create    ; Not always needed
				 )
				;; ;; used for org-gcal
				;; ("a" "Appointment" entry (file (concat org-directory "/gcal.org"))
				;;  "* %?\n\n%^T\n\n:PROPERTIES:\n\n:END:\n\n")
				("t" "Todo" entry (file+headline
													 (lambda () (expand-file-name "captures.org" org-directory))
													 "Todos")
				 "* TODO %?\t%^G\n :LOGBOOK:\n - CREATED: %U\n :END:\n\n About region:%i\n %a" :prepend t)
				("j" "Journal" entry (file+datetree (lambda ()(expand-file-name "journal.org" org-directory)))
				 "* %?\t%^G\n CREATED: %U\n About region:%i\n %a")
				("c" "Code" entry (file+headline 
													 (lambda () (expand-file-name "captures.org" org-directory ))
													 "Code")
				 "* %?\t%^G\n#+BEGIN_SRC %^{language}\n\n#+END_SRC\n CREATED: %U\n About region:%i\n %a" :prepend t)
				;; The following two templates are used by the org-capture extension in your web browser
				("p" "Protocol" entry (file+headline
															 (lambda () (expand-file-name "captures.org" org-directory ))
															 "Bookmarks from the web browser")
         "* [[%:link][%:description]]\nCREATED: %U\n\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n%?" :prepend t)
				("L" "Protocol Link" entry (file+headline
																		(lambda () (expand-file-name "captures.org" org-directory ))
																		"Bookmarks from the web browser")
         "* [[%:link][%:description]] \nCREATED: %U\n\n%?" :prepend t)
				))

Clocking and logging

Collect log entries in drawer:

(setq org-log-into-drawer t)

Clock out when quitting emacs:

;; Taken from https://emacs.stackexchange.com/a/38487/12336
(defun my/org-clock-query-out ()
  "Ask the user before clocking out.
This is a useful function for adding to `kill-emacs-query-functions'."
  (if (and
       (featurep 'org-clock)
       (funcall 'org-clocking-p)
       (y-or-n-p "You are currently clocking time, clock out? "))
      (org-clock-out) 
    t)) ;; only fails on keyboard quit or error

;; timeclock.el puts this on the wrong hook!
(add-hook 'kill-emacs-query-functions 'my/org-clock-query-out)

Only use hours and minutes as duration format in clocktables:

(setq org-duration-format (quote h:mm))

Configure mode line:

(setq org-clock-mode-line-total 'today)

Helper function to collect today’s clocked times:

(defun tl/org-agenda-files-clock-sum-today ()
	(apply '+ (mapcar
					 #'(lambda (file)
							(save-window-excursion
								(find-file file)
								(org-clock-sum-today)))
					 (org-agenda-files))))

org-mru-clock

https://github.com/unhammer/org-mru-clock

Pre-fill your clock history with clocks from your agenda files.

(use-package org-mru-clock
	:ensure t)

Download

org-download

  • State “TODO” from [2021-04-08 Thu 15:13]

https://github.com/abo-abo/org-download

(use-package org-download
	:ensure t
	:pin MELPA
	:config
	(setq org-download-image-org-width 400 ; will also add #+ATTR_ORG keyword
				org-download-method 'attach
				org-download-screenshot-method "scrot -s %s")
  (add-hook 'dired-mode-hook 'org-download-enable))

Export

Use a less intrusive export interface:

(setq org-export-dispatch-use-expert-ui t)

Note that the last export command on the current buffer can be reused with C-u C-c C-e.

Clipboard

Usually, when copying parts of an Org file to the system clipboard (i.e. with kill-ring-save) and inserting it in another application, the formatting (bold face, links, lists, code blocks, …) is not preserved, but the bare Org markup is shown. To preserve the formatting, the Org markup has to be exported to HTML first, and then copied to the clipboard.

For this, I’m adapting the solution proposed by John Kitchin for macOS. See also his short presentation on Youtube: https://www.youtube.com/watch?v=irkmQnggVpE

(defun org-copy-formatted-text-to-clipboard ()
  "Export region to HTML, and copy it to the clipboard."
  (interactive)
  (save-window-excursion
    (let* ((buf (org-export-to-buffer 'html "*Formatted Copy*" nil nil t t))
           (html (with-current-buffer buf (buffer-string))))
      (with-current-buffer buf
        (shell-command-on-region
         (point-min)
         (point-max)
				 ;; Command on macOS
         ;; "textutil -stdin -format html -convert rtf -stdout | pbcopy"
				 ;; Command on Linux
				 "xclip -verbose -selection clipboard -t text/html"
				 ))
      (kill-buffer buf))))
(define-key org-mode-map (kbd "s-w") 'org-copy-formatted-text-to-clipboard)

Note that xclip will wait (and Emacs will be stuck) until the copied content is pasted! I’m therefore using ox-clip instead, which is a little heavier.

ox-clip

https://github.com/jkitchin/ox-clip

Copy selected regions in Org buffers as formatted text to the clipboard. With ox-clip-image-to-clipboard, this also includes attached image files or LaTeX formulas which are first converted to images.

(use-package ox-clip
	:ensure t)
(define-key org-mode-map (kbd "s-w") 'tl/ox-clip-formatted-copy)

I add some custom filters for list items:

(defun tl/ox-clip-formatted-copy ()
  "Export the selected region to HTML to the clipboard using 
`ox-clip-formatted-copy', but with custom filters."
  (interactive)
  (let ((org-export-filter-plain-list-functions
         '(org-fm-export-list-item-to-html))
				(org-export-filter-final-output-functions
				 '(org-fm-export-whole-list-to-html)))
    (call-interactively 'ox-clip-formatted-copy)))

(define-key org-mode-map (kbd "s-w") 'tl/ox-clip-formatted-copy)

iCalendar

https://www.gnu.org/software/emacs/manual/html_node/org/iCalendar-Export.html

General options
(setq org-icalendar-timezone "Europe/Berlin"
			org-icalendar-include-todo nil			; Non-nil means create VTODO components from TODO items.
			org-icalendar-use-deadline '(event-if-todo ; Deadlines in TODO entries become calendar events.
																	 event-if-not-todo ; Deadlines in non-TODO entries become calendar events.
																	 todo-due					 ; Use deadlines in TODO entries as due-dates.
																	 ) 
			org-icalendar-use-scheduled '(;; todo-start ; Scheduling time stamps in TODO entries become start date.
																		;; event-if-todo ; Scheduling time stamps in TODO entries become an event.
																		;; event-if-not-todo ; Scheduling time stamps in non-TODO entries become an event.
																		) 
			org-icalendar-include-body nil)		; Amount of text below headline to be included in iCalendar export.

Helper function for removing timestamps from headers:

(defun tl/remove-org-timestamps (&optional file)
	"Remove all (exported) Org timestamps and surrounding blanks from buffer or FILE."
  (save-window-excursion
		(when file (find-file file))
		(beginning-of-buffer)
		(while (re-search-forward
						(concat "\\([[:blank:]]\\)*"
										"\\(" org-element--timestamp-regexp "\\)"
										"\\(–\\|[[:blank:]]\\)*"
										) nil t)
			(replace-match ""))
		(when file
			(save-buffer)
			(kill-buffer))
		))

Helper function for removing old dates in an icalender file:

(defun tl/icalendar-remove-dates-older-than (&optional date-string-Ymd)
	"Remove all events older than DATE-STRING-YMD in an icalendar/ics buffer.

The argument DATE-STRING-YMD is optional.  If not specified, it is set 90 days
before current time."
	(beginning-of-buffer)
	(let ((date-string-Ymd (or date-string-Ymd
														 (format-time-string "%Y%m%d"
																								 (let ((time (decode-time (current-time)))
																											 (delta (make-decoded-time :day -90)))
																									 (encode-time (decoded-time-add time delta)))
																								 )
														 ;; (format-time-string "%Y%m%d"
														 ;; 										 (time-subtract
														 ;; 											(current-time)
														 ;; 											(seconds-to-time (* 60 60 24 90))))
														 )
												 ))
		(while (re-search-forward "DTSTART\\(?:;VALUE=DATE\\)?:[[:blank:]]*\\(.\\{8\\}\\)" nil t)
			(when (< (string-to-number (match-string 1))
							 (string-to-number date-string-Ymd))
				(delete-region
				 (re-search-backward "BEGIN:VEVENT")
				 (re-search-forward "END:VEVENT"))
				;; Remove empty line
				(if (save-excursion
							(beginning-of-line)
							(looking-at-p "[[:blank:]]*$"))
						(delete-line)
					())
				))))
Export of org-agenda

I now and then export the dates of my org-agenda and push them to a CalDAV server with vdirsyncer.

See blog post Integrating Org mode Agenda into other calendar apps.

The org-mode agenda can be exported with two commands:

  • org-icalendar-export-agenda-files: export to separate files.
  • org-icalendar-combine-agenda-files: combine all exported entries in one file, which is specified in org-icalendar-combined-agenda-file (~/org.ics by default).

The idea is to export the agenda to an icalendar file (when saving an agenda file) that is regularly imported to the calendar.

(defun tl/org-export-agenda-to-ics ()
	(interactive)
	(if (org-agenda-files)
			(let* (
						 (org-export-with-broken-links t) ; Ignore broken links
						 (org-agenda-start-day "-2w") ; Unfortunately, this does not limit the export to recent dates 
						 )
				(org-icalendar-combine-agenda-files) ; Non-nil argument for asynchronuous processing 
				(message (concat "org-agenda exported to "  org-icalendar-combined-agenda-file))
				(save-window-excursion
					(find-file org-icalendar-combined-agenda-file)
					(tl/remove-org-timestamps)
					(tl/icalendar-remove-dates-older-than)
					(save-buffer)
					(kill-buffer))
				(message (concat "org-agenda calendar file cleaned and saved: " org-icalendar-combined-agenda-file)))
		(message "tl/org-export-agenda-to-ics: org-agenda-files not specified, export aborted.")
		)
	)

The export can be narrowed to specific org-agenda files in the following way:

(let ((org-agenda-files (list
												 "/path/to/my/org-file.org"))
			(org-icalendar-combined-agenda-file "/path/to/my/ics-file.ics"))
	(org-icalendar-combine-agenda-files))

Lets create a helper function that can exports an org-mode buffer to an ics file with the same path:

(defun tl/org-export-buffer-to-ics ()
	(interactive)
	(if (buffer-file-name (current-buffer))
			(let* ((file-name (buffer-file-name (current-buffer)))
						 (org-agenda-files (list file-name))
						 (org-icalendar-combined-agenda-file
							(concat
							 (file-name-sans-extension file-name)
							 ".ics")))
				(tl/org-export-agenda-to-ics))
		(message "tl/org-export-buffer-to-ics: buffer has no file, export aborted.")
		))

Finally, wrap a function around the export of the org-agenda and syncing it with a CalDAV server using vdirsyncer.

(defun tl/org-export-agenda-to-caldav ()
	"Export `org-agenda' to icalendar/ics file and upload it to CalDAV server."
	(interactive)
	(message "Exporting org-agenda to ics file...")
	(tl/org-export-agenda-to-ics)
	(message "Exporting org-agenda to ics file finished.")
	(message "Syncing org-agenda and CalDav server...")
	(shell-command "vdirsyncer sync org_agenda_calendar")
	(while (yes-or-no-p "Sync with vdirsyncer once more? ")
		(shell-command "vdirsyncer sync org_agenda_calendar"))
	(message "Syncing org-agenda and CalDav server finished.")
	)
Export of buffer or heading

There is a function org-icalendar-export-to-ics, which exports a buffer or region to a single iCalendar file.

Let’s define a robuster function that exports just the heading under point and removes Org timestamps:

(defun tl/org-icalendar-export-heading-to-ics () 
	"Export heading under point to ics file."
	(interactive)
	(let* ((org-export-with-broken-links t)
				 (org-icalendar-after-save-hook 'tl/remove-org-timestamps)
				 (org-icalendar-store-UID t)
				 (org-icalendar-categories nil)
				 (org-icalendar-include-body t))
		;; (org-icalendar-export-to-ics nil t) ; Only works for regions
		(save-restriction
			(widen)
			(org-narrow-to-subtree)
			;; Add property EXPORT_FILE_NAME, which org-icalendar-export-to-ics will read when
			;; SUBTREEP is t.
			(unless (org-entry-get nil "EXPORT_FILE_NAME")
				(org-set-property "EXPORT_FILE_NAME"
													(concat
													 (read-directory-name "Select directory to safe ics file: "
																								(expand-file-name "~/Downloads/"))
													 (org-id-get-create)
													 ".ics")))
			;; Mark subtree if there is no active region. If SUBTREEP is t, only marked regions are exported.
			(beginning-of-buffer)
			(unless (region-active-p) (save-excursion (org-mark-subtree)))
			(org-icalendar-export-to-ics nil t) ; If SUBTREEP is t.
			(deactivate-mark)
			(widen))))

Work in progress:

(defun org-icalendar--add-status (entry)
  "Return the iCalendar STATUS field based on the :STATUS: property."
  (let ((status (org-entry-get (point) "STATUS")))
    (cond
     ((equal status "TENTATIVE") "STATUS:TENTATIVE\n")
     ((equal status "CONFIRMED") "STATUS:CONFIRMED\n")
     ((equal status "CANCELLED") "STATUS:CANCELLED\n")
     (t nil))))

(advice-add 'org-icalendar--vevent :around
            (lambda (orig-fn &rest args)
              "Include STATUS in iCalendar export within VEVENT."
              (let* ((event (apply orig-fn args))
                     (status (org-icalendar--add-status (car args))))
                (if status
                    ;; Insert the STATUS line after the SUMMARY line
                    (replace-regexp-in-string
                     "\\(SUMMARY:.*\\)"
                     (concat "\\1\n" status)
                     event)
                  event))))

ox-hugo

https://ox-hugo.scripter.co/

Export to Hugo-compatible markdown.

(use-package ox-hugo
	:pin MELPA
  :ensure t
  :after ox)

ox-reveal

https://github.com/yjwen/org-reveal/ https://github.com/hexmode/ox-reveal

Turn org-mode buffers into Reveal slides. Requires htmlize.

(use-package ox-reveal
	:ensure t
	:config 
	(setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js@4.6.1/dist/reveal.min.js"
				org-reveal-mathjax t)
	)

OpenDocument Text (ODT)

Images

Only use actual width of image when not specified with #+ATTR* :width (requires imagemagick)

(setq org-image-actual-width nil)

Set background color of images to white. Taken from https://emacs.stackexchange.com/a/37927/12336

(defcustom org-inline-image-background nil
	"The color used as the default background for inline images.
When nil, use the default face background."
	:group 'org
	:type '(choice color (const nil)))

(defun create-image-with-background-color (args)
  "Specify background color of Org-mode inline image through modify `ARGS'."
  (let* ((file (car args))
         (type (cadr args))
         (data-p (caddr args))
         (props (cdddr args)))
    ;; Get this return result style from `create-image'.
    (append (list file type data-p)
            (list :background (or org-inline-image-background (face-background 'default)))
            props)))

(advice-add 'create-image :filter-args
            #'create-image-with-background-color)

(require 'mode-local)
(setq-mode-local org-mode org-inline-image-background "white")

Import

Clipboard

When inserting formatted content from the clipboard, this is first converted to appropriate Org markup:

(defun org-insert-formatted-text-from-clipboard ()
	"Insert formatted clipboard content with Org markup." 
	(interactive)
	(shell-command "xclip -o -t text/html | pandoc -f html -t org -" '(4)))
(define-key org-mode-map (kbd "s-y") 'org-insert-formatted-text-from-clipboard)

iCalendar

When receiving an ics file by mail, this is handled by mu4e.

LaTeX support

General Settings

Load export module for LaTeX:

(require 'ox-latex)

Headlines with :ignore: tag are ignored during export, but not their body:

(require 'ox-extra)
(ox-extras-activate '(ignore-headlines))

Support from org-babel:

;; (org-babel-do-load-languages 'org-babel-load-languages '((latex . t)))
(setq org-highlight-latex-and-related '(latex script entities)) ; inline sytax highlighting
;; (add-to-list 'org-latex-packages-alist '("" "tikz" t))					; unfortunately this breaks the color of fonts in inline previews
;; (add-to-list 'org-latex-packages-alist '("" "forest" t))

LaTeX source blocks should be executed with the following header arguments:

#+PROPERTY: header-args:latex+ :dir graphics  :packages '(("" "../text-template/packages/tikz-settings")("" "times")) :headers '("\\input{../text-template/myMacros}")
...
...

Style of LaTeX previews:

(let ((default-scale 1.8))
	;; static
	(plist-put org-format-latex-options :scale default-scale) ; scale inline PNGs
	(plist-put org-format-latex-options :background 'default) ; background of inline PNGs

	;; dynamic (http://emacs.stackexchange.com/a/13032/12336)
	(defun update-org-latex-fragment-scale ()
		(let ((text-scale-factor (expt text-scale-mode-step text-scale-mode-amount)))
			(plist-put org-format-latex-options :scale (* 2.3 default-scale text-scale-factor)))
		)
	(add-hook 'text-scale-mode-hook 'update-org-latex-fragment-scale)
	)

Use LaTeXmk:

(setq org-latex-pdf-process (list "latexmk -f -pdf %f -outdir=%o"))
;; The option "-cd %o" is needed when executing babel source blocks, 
;; during which the auxiliary files are moved to some temporary directory.
;; Instead of "-cd %o", one could also use "-outdir=%o". 
;; See the discussion here:
;; https://github.com/fniessen/refcard-org-beamer/commit/9f75e013127940e793e0a925fc4ee222bae0e45c

Extra function to delete auxiliary files:

(defun delete-org-latex-aux-files ()
	"This function deletes auxiliary files that are not deleted by `latexmk` or `TeX-clean`."
	(interactive)
	(shell-command "rm -rfv *.fls *.prv preview.fmt .aux .fdb_latexmk frag-master.tex"))

Adjust org-format-latex-header:

(let ((default org-format-latex-header))
	(setq org-format-latex-header (concat default "
\\DeclareMathOperator*{\\argmax}{arg\\,max}
\\DeclareMathOperator*{\\argmin}{arg\\,min}
")))

Add some classes to the set of known classes:

(add-to-list 'org-latex-classes
         '("tl-abstract" 
						"\\documentclass{article}
						[NO-DEFAULT-PACKAGES]"
            ("\\paragraph{%s}" . "\\paragraph*{%s}")
            ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

(add-to-list 'org-latex-classes
         '("tl-article" 
						"\\documentclass{scrartcl}
						[NO-DEFAULT-PACKAGES]"
						("\\section{%s}" . "\\section*{%s}") 
						("\\subsection{%s}" . "\\subsection*{%s}") 
						("\\subsubsection{%s}" . "\\subsubsection*{%s}")
						("\\paragraph{%s}" . "\\paragraph*{%s}")))

Add the class langscibook to the set of known classes:

(add-to-list 'org-latex-classes
						 '("langscibook" 
							 "\\documentclass{langscibook}
				    		[NO-DEFAULT-PACKAGES]" 
							 ("\\part{%s}" . "\\part*{%s}") 
							 ("\\chapter{%s}" . "\\chapter*{%s}") 
							 ("\\section{%s}" . "\\section*{%s}") 
							 ("\\subsection{%s}" . "\\subsection*{%s}") 
							 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
							 ("\\paragraph{%s}" . "\\paragraph*{%s}")
							 ))

(add-to-list 'org-latex-classes
						 '("langscibook-paper" 
							 "\\documentclass[output=paper]{langscibook}
				    		[NO-DEFAULT-PACKAGES]" 
							 ("\\section{%s}" . "\\section*{%s}") 
							 ("\\subsection{%s}" . "\\subsection*{%s}") 
							 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
							 ("\\paragraph{%s}" . "\\paragraph*{%s}")
							 ))

Add the class jlm to the set of known classes:

(add-to-list 'org-latex-classes
						 '("jlm" 
							 "\\documentclass{jlm}
				    		[NO-DEFAULT-PACKAGES]" 
							 ("\\section{%s}" . "\\section*{%s}") 
							 ("\\subsection{%s}" . "\\subsection*{%s}") 
							 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
							 ("\\paragraph{%s}" . "\\paragraph*{%s}")
							 ))

Use #+NAME: field as argument of \label during export:

(setq org-latex-prefer-user-labels t)

Put \label outside and after \caption (see https://emacs.stackexchange.com/a/51397/12336):

(defun org-latex--caption/label-string (element info)
  "Return caption and label LaTeX string for ELEMENT.

INFO is a plist holding contextual information.  If there's no
caption nor label, return the empty string.

For non-floats, see `org-latex--wrap-label'."
  (let* ((label (org-latex--label element info nil t))
				 (main (org-export-get-caption element))
				 (attr (org-export-read-attribute :attr_latex element))
				 (type (org-element-type element))
				 (nonfloat (or (and (plist-member attr :float)
														(not (plist-get attr :float))
														main)
											 (and (eq type 'src-block)
														(not (plist-get attr :float))
														(null (plist-get info :latex-listings)))))
				 (short (org-export-get-caption element t))
				 (caption-from-attr-latex (plist-get attr :caption)))
    (cond
     ((org-string-nw-p caption-from-attr-latex)
      (concat caption-from-attr-latex "\n"))
     ((and (not main) (equal label "")) "")
     ((not main) label)
     ;; Option caption format with short name.
     (t
      (format (if nonfloat "\\captionof{%s}%s{%s}\n%s"
								"\\caption%s%s{%s}\n%s")
							(let ((type* (if (eq type 'latex-environment)
															 (org-latex--environment-type element)
														 type)))
								(if nonfloat
										(cl-case type*
											(paragraph "figure")
											(image "figure")
											(special-block "figure")
											(src-block (if (plist-get info :latex-listings)
																		 "listing"
																	 "figure"))
											(t (symbol-name type*)))
									""))
							(if short (format "[%s]" (org-export-data short info)) "")
							(org-export-data main info)
							label)))))

Beamer

Documentation:

Load support for exporting LaTeX beamer presentations:

(require 'ox-beamer)

Add virtual beamer class (tl-beamer) to the known LaTeX classes:

(add-to-list 'org-latex-classes
         '("tl-beamer" 
						"\\documentclass{beamer}
						[NO-DEFAULT-PACKAGES]"
            ("\\section{%s}" . "\\section*{%s}")
            ("\\subsection{%s}" . "\\subsection*{%s}")
            ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
            ("\\paragraph{%s}" . "\\paragraph*{%s}")
            ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

Change export of bold font:

(defun my-beamer-bold (contents backend info)
  (when (eq backend 'beamer)
    (replace-regexp-in-string "\\`\\\\[A-Za-z0-9]+" "\\\\textbf" contents)))

(add-to-list 'org-export-filter-bold-functions 'my-beamer-bold)

Change export of italic font:

(defun my-beamer-italic (contents backend info)
  (when (eq backend 'beamer)
    (replace-regexp-in-string "\\`\\\\[A-Za-z0-9]+" "\\\\textit" contents)))

(add-to-list 'org-export-filter-italic-functions 'my-beamer-italic)

org-fragtog

https://github.com/io12/org-fragtog

Automatically toggle LaTeX fragment previews under point.

(use-package org-fragtog
	:ensure t
	;; :config
	;; (add-hook 'org-mode-hook 'org-fragtog-mode)
	)

Links

Follow links when pressing <return>:

(setq org-return-follows-link t)

Links to files are opened in the same window, i.e. the window does not get split:

(setq org-link-frame-setup '((file . find-file)))

Show whether links to files are valid:

;; taken from https://emacs.stackexchange.com/a/33078/12336
(org-link-set-parameters
 "file"
 :face (lambda (path) (when (not (file-remote-p path))(if (file-exists-p path) 'org-link 'org-warning))))

(org-link-set-parameters
 "attachment"
 :face (lambda (path) (when (not (file-remote-p path))(if (file-exists-p (expand-file-name path (org-attach-dir))) 'org-link 'org-warning))))

Use IDs rather than headline names:

(setq org-id-link-to-org-use-id 'create-if-interactive)

create-if-interactive is chosen, because, with t, unwanted ID properties would be inserted when tangling org-babel source blocks. From the description:

create-if-interactive If ‘org-store-link’ is called directly (interactively, as a user command), do create an ID to support the link. But when doing the job for capture, only use the ID if it already exists. The purpose of this setting is to avoid proliferation of unwanted IDs, just because you happen to be in an Org file when you call ‘org-capture’ that automatically and preemptively creates a link. If you do want to get an ID link in a capture template to an entry not having an ID, create it first by explicitly creating a link to it, using ‘C-c l’ first.

Use ID completion when generating ID links:

;; taken from https://emacs.stackexchange.com/a/12434/12336
(defun org-id-complete-link (&optional arg)
  "Create an id: link using completion"
  (concat "id:"
          (org-id-get-with-outline-path-completion)))

(org-link-set-parameters "id"
                         :complete 'org-id-complete-link)

Function to remove all ID properties from buffer:

(defun tl/org-id-remove-from-buffer ()
	"Remove/delete all ID entries from current buffer and update the databases."
	(interactive)
	(save-excursion
		(beginning-of-buffer)
		(let ((id-removed-p nil))
			(when (not (org-at-heading-p))
				(org-next-visible-heading 1))
			;; org-next-visible-heading moves point to end of buffer after last heading
			(while (not (eobp)) 
				(when (org-entry-delete (point) "ID")
					(setq id-removed-p t))
				(org-next-visible-heading 1))
			(when (symbol-value 'id-removed-p)
				(org-id-update-id-locations)))))

Function to replace a link with its description, inspired by https://emacs.stackexchange.com/a/10712/12336:

(defun tl/org-replace-link-with-description ()
	"Replace an Org link of the format [[LINK][DESCRIPTION]] with DESCRIPTION.
If the link is of the format [[LINK]], delete the whole org link.

In both the cases, save the LINK to the kill-ring.

Execute this command when point is at an Org link."
	(interactive)
	(when (and (derived-mode-p 'org-mode)
						 (org-in-regexp org-link-bracket-re))
		(save-excursion
			(let* ((link-range (org-in-regexp org-link-bracket-re 1))
						 (link-start-pos (car link-range))
						 (link-end-pos (cdr link-range))
						 (link (match-string 0))
						 (target (match-string 1))
						 (description (match-string 2))
						 ;; (description-start-pos (match-beginning 2))
						 ;; (description-end-pos (match-end 2))
						 )
				(kill-new (substring-no-properties target)) ; Save the link target to kill-ring
				(delete-region link-start-pos link-end-pos)
				(insert description)))))

;; (defun my/org-delete-link ()
;;   "Replace an org link of the format [[LINK][DESCRIPTION]] with DESCRIPTION.
;; If the link is of the format [[LINK]], delete the whole org link.

;; In both the cases, save the LINK to the kill-ring.

;; Execute this command while the point is on or after the hyper-linked org link."
;;   (interactive)
;;   (when (derived-mode-p 'org-mode)
;;     (let ((search-invisible t) start end)
;;       (save-excursion
;;         (when (re-search-backward "\\[\\[" nil :noerror)
;;           (when (re-search-forward "\\[\\[\\(.*?\\)\\(\\]\\[.*?\\)*\\]\\]" nil :noerror)
;;             (setq start (match-beginning 0))
;;             (setq end   (match-end 0))
;;             (kill-new (match-string-no-properties 1)) ; Save the link to kill-ring
;;             (replace-regexp "\\[\\[.*?\\(\\]\\[\\(.*?\\)\\)*\\]\\]" "\\2" nil start end)))))))

Open file types with specific programs:

(add-to-list 'org-file-apps '("\\.xopp\\'" . "xournalpp %s"))

Function to open links in new frame:

(defun tl/org-open-in-new-frame ()
	"Open thing at point in new frame."
	(interactive)
	(let ((new-frame (make-frame)))
    (select-frame-set-input-focus new-frame)
		(org-open-at-point)
		(recenter)))

Added to hydra-org.

  • [ ] Also add it to the mouse context menu!

Deactivate timestamps in link description by replacing angular brackets. The following approach advices org-insert-link:

(defun tl/org-link-deactivate-timestamp ()
	"Deactivate timestamp in Org link description by replacing angular brackets with curly braces."
	(interactive)
	(when (and (derived-mode-p 'org-mode)
	 (org-in-regexp org-link-bracket-re))
		(save-excursion
	(let* ((link-range (org-in-regexp org-link-bracket-re 1))
	 (link-start-pos (car link-range))
	 (link-end-pos (cdr link-range))
	 (link (match-string 0))
	 (target (match-string 1))
	 (description (match-string 2))
	 (description-start-pos (match-beginning 2))
	 (description-end-pos (match-end 2)))
				(goto-char description-start-pos)
				(while (re-search-forward org-element--timestamp-regexp description-end-pos t)
					(let* ((timestamp-start-pos (match-beginning 0))
								 (timestamp-end-pos (match-end 0)))
						(replace-regexp "<" "{" nil timestamp-start-pos timestamp-end-pos t)
						(replace-regexp ">" "}" nil timestamp-start-pos timestamp-end-pos t)
						))))))

(advice-add 'org-insert-link :after #'tl/org-link-deactivate-timestamp)

Alternatively, one could change the heading before generating the link with org-store-link. However, this is more complicated and creates other problems, for example, when using the capture mechanism:

(defun tl/org-store-link-advice (orig-fun &rest args)
	"Modify Org heading before creating an Org link.

Timestamps are deactivated by replacing angular brackets with curly braces."
	(interactive)
	;; (when (derived-mode-p 'org-mode)
	(if (and (eq major-mode 'org-mode)
					 (org-current-level)					; t if below heading
					 ;; Heading has a timestamp?
					 (save-excursion
             (org-back-to-heading t)
             (re-search-forward org-element--timestamp-regexp (line-end-position) t)))
			(progn
				(org-id-get nil t)													; Create ID if missing
				(org-copy-subtree)
				(save-window-excursion
					(let ((temp-file (make-temp-file "-temp-buffer-")))
						(find-file temp-file)
						(yank)
						(org-mode)
						(goto-char (point-min))
						(while (re-search-forward org-element--timestamp-regexp (line-end-position) t)
							(let* ((timestamp-start-pos (match-beginning 0))
										 (timestamp-end-pos (match-end 0)))
								(replace-regexp "<" "{" nil timestamp-start-pos timestamp-end-pos t)
								(replace-regexp ">" "}" nil timestamp-start-pos timestamp-end-pos t)
								))
						(save-buffer)
						;; (org-run-like-in-org-mode orig-fun)
						(call-interactively orig-fun) ; FIXME: issue with non-interactive calls
						(let ((buffer-offer-save nil))
							(kill-buffer)))))
		;; (org-run-like-in-org-mode orig-fun)
		(call-interactively orig-fun) ; FIXME: issue with non-interactive calls
		))

(advice-add 'org-store-link :around #'tl/org-store-link-advice)

Obsolete

Function to update link syntax that got changed in org-mode v9.3 (taken from https://orgmode.org/Changes.html):

(defun org-update-link-syntax (&optional no-query)
  "Update syntax for links in current buffer.
Query before replacing a link, unless optional argument NO-QUERY
is non-nil."
  (interactive "P")
  (org-with-point-at 1
    (let ((case-fold-search t))
      (while (re-search-forward "\\[\\[[^]]*?%\\(?:2[05]\\|5[BD]\\)" nil t)
        (let ((object (save-match-data (org-element-context))))
          (when (and (eq 'link (org-element-type object))
                     (= (match-beginning 0)
                        (org-element-property :begin object)))
            (goto-char (org-element-property :end object))
            (let* ((uri-start (+ 2 (match-beginning 0)))
                   (uri-end (save-excursion
                              (goto-char uri-start)
                              (re-search-forward "\\][][]" nil t)
                              (match-beginning 0)))
                   (uri (buffer-substring-no-properties uri-start uri-end)))
              (when (or no-query
                        (y-or-n-p
                         (format "Possibly obsolete URI syntax: %S.  Fix? "
                                 uri)))
                (setf (buffer-substring uri-start uri-end)
                      (org-link-escape (org-link-decode uri)))))))))))

Listings

Use the listings package:

(setq org-latex-listings 'listings)

Map scr-block languages to listings environments:

;; (setq org-latex-custom-lang-environments
;; 			'((org-babel-language "listings-environment")))

Makefile support

;; (org-babel-do-load-languages 'org-babel-load-languages '((makefile . t)))

Memacs

https://github.com/novoid/Memacs https://arxiv.org/pdf/1304.1332.pdf

Minutes

Blog posts:

org-fm

https://github.com/timmli/org-fm-dev

Fast minutes with org-fm.

;; (add-to-list 'load-path (expand-file-name "org-fm-dev" lisp-dir))
(use-package org-fm
	:load-path "lisp/org-fm-dev"
	:config
	(org-fm-minor-mode))

HTML export of org-fm is used in ox-clip.

Modules

See http://orgmode.org/worg/org-contrib/

;; (setq org-modules '( ;; org-bbdb
;;                       ;; org-gnus
;;                       ;; org-drill
;;                       ;; org-info
;;                       ;; org-jsinfo
;;                       ;; org-habit
;;                       ;; org-irc
;;                       ;; org-mouse
;;                       ;; org-protocol
;;                       ;; org-annotate-file
;;                       ;; org-eval
;;                       ;; org-expiry
;;                       ;; org-interactive-query
;;                       ;; org-man
;;                       ;; org-collector
;;                       ;; org-panel
;;                       ;; org-screen
;;                       ;; org-toc
;; 											))
;; (eval-after-load 'org
;;  '(org-load-modules-maybe t))
;; (setq org-expiry-inactive-timestamps t)

Newline/<return>

Split the line with M-<return>. Disables org-table-wrap-region when set to nil.

(setq org-M-RET-may-split-line '((default . t)))

scimax/org-return

The following is taken from http://github.com/jkitchin/scimax/blob/master/scimax-org.el . See also http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ .

(require 'org-inlinetask)

(defun scimax/org-return (&optional ignore)
  "Add new list item, heading or table row with RET.
A double return on an empty element deletes it.
Use a prefix arg to get regular RET. "
  (interactive "P")
  (if ignore
      (org-return)
    (cond

     ((eq 'line-break (car (org-element-context)))
      (org-return t))

     ;; Open links like usual, unless point is at the end of a line.
     ;; and if at beginning of line, just press enter.
     ((or (and (eq 'link (car (org-element-context))) (not (eolp)))
					(bolp))
      (org-return))

     ;; It doesn't make sense to add headings in inline tasks. Thanks Anders
     ;; Johansson!
     ((org-inlinetask-in-task-p)
      (org-return))

     ;; checkboxes - add new or delete empty
     ((org-at-item-checkbox-p)
      (cond
       ;; at the end of a line.
       ((and (eolp)
						 (not (eq 'item (car (org-element-context)))))
				(org-insert-todo-heading nil))
       ;; no content, delete
       ((and (eolp) (eq 'item (car (org-element-context))))
				(setf (buffer-substring (line-beginning-position) (point)) ""))
       ((eq 'paragraph (car (org-element-context)))
				(goto-char (org-element-property :end (org-element-context)))
				(org-insert-todo-heading nil))
       (t
				(org-return))))

     ;; lists end with two blank lines, so we need to make sure we are also not
     ;; at the beginning of a line to avoid a loop where a new entry gets
     ;; created with only one blank line.
     ((org-in-item-p)
      (cond
       ;; empty definition list
       ((and (looking-at " ::")
						 (looking-back "- " 3))
				(beginning-of-line)
				(delete-region (line-beginning-position) (line-end-position)))
       ;; empty item
       ((and (looking-at "$")
						 (looking-back "- " 3))
				(beginning-of-line)
				(delete-region (line-beginning-position) (line-end-position)))
       ;; ;; numbered list (original)
       ;; ((and (looking-at "$")
			 ;; 			 (looking-back "[0-9]+. " (line-beginning-position)))
			 ;; 	(beginning-of-line)
			 ;; 	(delete-region (line-beginning-position) (line-end-position)))
			 ;; numbered or alphabetical list (changed by TL)
       ((save-excursion
					(beginning-of-line)
					(or (looking-at " *[0-9]+. *$")
							(looking-at " *[a-zA-Z]. *$")))
				(beginning-of-line)
				(delete-region (line-beginning-position) (line-end-position)))
       ;; insert new item
       (t
				(end-of-line)
				(org-insert-item))))

     ;; org-heading
     ((org-at-heading-p)
      (if (not (string= "" (org-element-property :title (org-element-context))))
					(progn
						;; Go to end of subtree suggested by Pablo GG on Disqus post.
						(org-end-of-subtree)
						(org-insert-heading-respect-content)
						(outline-show-entry))
				;; The heading was empty, so we delete it
				(beginning-of-line)
				(setf (buffer-substring
							 (line-beginning-position) (line-end-position)) "")))

     ;; tables
     ((org-at-table-p)
      (if (-any?
					 (lambda (x) (not (string= "" x)))
					 (nth
						(- (org-table-current-dline) 1)
						(remove 'hline (org-table-to-lisp))))
					(org-return)
				;; empty row
				(beginning-of-line)
				(setf (buffer-substring
							 (line-beginning-position) (line-end-position)) "")
				(org-return)))

     ;; fall-through case
     (t
      (org-return)))))

Set keys:

(with-eval-after-load 'org
	(define-key org-mode-map (kbd "<return>") 'scimax/org-return)) ; not org-table-next-row

Shift-return

(defun tl/org-shift-return ()
	(interactive)
  (cond
	 ;; in org-table
	 ((org-table-p)
		(org-table-copy-down 1))
	 ;; else
	 (t (smart-open-line))
	 )
  )

Set keys:

(with-eval-after-load 'org
	(define-key org-mode-map (kbd "S-<return>") 'tl/org-shift-return )) ; not org-table-copy-down

org-appear

  • State “TODO” from [2023-10-28 Sat 11:33]

https://github.com/awth13/org-appear

Automatically toggle visibility of emphasis markers and links under point.

Issues

(use-package org-appear
	:ensure t
	:pin MELPA
	:after org
	:config
	(add-hook 'org-mode-hook 'org-appear-mode)
	(setq org-appear-autoemphasis t
				org-appear-autolinks t
				org-appear-autosubmarkers t))

org-babel

Preserve indentation in source blocks:

(setq org-src-preserve-indentation t)

Don’t ask for confirmation when evaluating source blocks:

(setq org-confirm-babel-evaluate nil)

Fontify source blocks as defined for the enclosed language:

(setq org-src-fontify-natively t)

Use tab key as defined for the enclosed language:

(setq org-src-tab-acts-natively t)

The following fixes a problem with gnuplot-mode under windows. Alternatively, one could add `:session none` to every source block. See https://www.mail-archive.com/emacs-orgmode@gnu.org/msg30416.html

(when (eq system-type 'windows-nt)
	(setq org-babel-default-header-args:gnuplot
				'((:results . "file")
					(:exports . "results"))))

Add org-mode to the list of supported languages:

;; (org-babel-do-load-languages 'org-babel-load-languages '((org . t)))

Gnuplot support

https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-gnuplot.html

;; (org-babel-do-load-languages 'org-babel-load-languages '((gnuplot . t)))

Gnuplot example:

reset

set title "a simple graph"

set xrange [0:1]
set autoscale y
set xlabel "frequency of A"

plot x * x title 'AA', (1-x) * (1-x) title 'aa', 2 * x * (1-x) title 'Aa'  

Graphviz/dot support

(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))
;; (org-babel-do-load-languages  'org-babel-load-languages '((dot . t)))

LaTeX support

The LaTeX compiler is specified in org-latex-compiler or buffer-locally in #+LATEX_COMPILER.

Plantuml support

https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-plantuml.html

;; (org-babel-do-load-languages  'org-babel-load-languages '((plantuml . t)))

PlantUML example:

Bob -> Alice : Hello World!

Python support

emacs-jupyter

https://github.com/nnicandro/emacs-jupyter

Provides an API for creating Jupyter kernel frontends in Emacs and also provides REPL and org-mode source block based frontends.

(use-package jupyter
  :ensure t
  :defer t
  :config ; Do not use :init here, because this will throw "Error (use-package): jupyter/:init"
  (setq org-babel-default-header-args:jupyter-python
				'((:async . "yes")
					(:session . "py")
					(:kernel . "python3")))
	;; (org-babel-do-load-languages 'org-babel-load-languages
	;; 														 '((emacs-lisp . t)
	;; 															 (python . t)
	;; 															 (jupyter . t)))
	)

R support

https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-R.html

Requires ess.

;; (org-babel-do-load-languages 'org-babel-load-languages '((R . t)))
;; (setq org-babel-R-command "R --slave --no-save") ; needed on Windows

R example (from https://r-lang.com/plot-function-in-r-with-example/):

x <- seq(-pi, pi, 0.1)

plot(x, cos(x), type = "l",
 main = "Overlaying Charts",
 ylab = "",
 col = "red")

lines(x, sin(x), col = "blue")

legend("topleft", c("sin(x)", "cos(x)"), fill = c("blue", "red"))

Conf support

Org-babel source blocks containting the conf format. Just export them to a file using :results file keyword.

(defvar org-babel-default-header-args:conf '())

(defun org-babel-execute:conf (body _params)
  "Execute a block of conf code.
This function is called by `org-babel-execute-src-block'."
  body)

(defun org-babel-prep-session:conf (_session _params)
  "Signal error; conf does not support sessions."
  (error "Conf file sessions are nonsensical"))

org-babel-load-languages

Load languages that can be evaluated in org-babel source blocks.

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (dot . t)
   (gnuplot . t)
   (latex . t)
	 (makefile . t)
	 (org . t)
	 (python . t)
	 (jupyter . t)												; load jupyter after python
	 (plantuml . t)
   (R . t)
	 (shell . t)))

org-drill

https://gitlab.com/phillord/org-drill/ https://orgmode.org/worg/org-contrib/org-drill.html

Drill yourself in everything you want to learn – inspired by Anki.

(use-package org-drill
	:ensure t)

org-drill-table

https://github.com/chrisbarrett/org-drill-table

Easily generate org-drill cards using org-mode tables.

(use-package org-drill-table
	:ensure t)

org-krita

  • State “TODO” from [2021-01-06 Wed 09:38]

https://github.com/lepisma/org-krita

org-protocol

https://orgmode.org/worg/org-contrib/org-protocol.html

(require 'org-protocol)

In order to make it available to, e.g., web browsers as an application, execute the following:

cat > "${HOME}/.local/share/applications/org-protocol.desktop" << EOF
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
EOF
update-desktop-database ~/.local/share/applications/
xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol

org-ref

https://github.com/jkitchin/org-ref

Add all kinds of reference types (citations, text references, indexes, …) to Org.

(use-package org-ref
	:ensure t
	:pin MELPA ; v2.0.0 is not compatible with helm v3.9.1
	:after (org helm)
	:init
	(require 'org-ref) 										; don't know why I need this
	(setq org-ref-default-bibliography (list user-bibliography-file)
				;; org-ref-bibliography-notes "~/bibliography/notes.org"
				org-ref-pdf-directory user-bibliography-pdf-dir
				org-ref-prefer-bracket-links t
				org-ref-default-ref-type "autoref"
				)
	:config
	(define-key org-mode-map (kbd "C-c ]") org-ref-insert-cite-function)
	(define-key org-mode-map (kbd "C-c )") org-ref-insert-ref-function)
	(define-key org-mode-map (kbd "C-c (") org-ref-insert-label-function)
	;; :bind (:map org-mode-map
	;; ("C-c ]" . org-ref-helm-insert-cite-link) ; Obsolete since > v2.0.0
	;; ("C-c )" . org-ref-helm-insert-ref-link)	; Obsolete since > v2.0.0
	;; ("C-c (" . org-ref-helm-insert-label-link) ; Obsolete since > v2.0.0
	;; )
	)

Add \citeauthoryear and \citealtauthoryear to org-ref:

;; Obsolete since > v2.0.0
;; (org-ref-define-citation-link "citeauthoryear")
;; (add-to-list 'org-ref-cite-types "citeauthoryear")
;; (org-ref-define-citation-link "citealtauthoryear")
;; (add-to-list 'org-ref-cite-types "citealtauthoryear")

Set colors:

(custom-set-variables
 '(org-ref-label-color "magenta")
 '(org-ref-ref-color "LimeGreen")
 '(org-ref-cite-color "ForestGreen")
 )

TODO: Change format entry string with org-ref-formatted-citation-formats.

org-roam

https://github.com/org-roam/org-roam

Knowledge management system similar to Roam and Zettelkästen. This configuration works with v2 of org-roam.

Example of how it works: https://blog.jethro.dev/posts/how_to_take_smart_notes_org/

Installation & configuration

Issues

  • [ ] The face specified with propertize is not shown in org-roam-find-file.
(use-package org-roam
  :ensure t
  :pin MELPA
	:init
	(setq org-roam-v2-ack t)
  :custom
  (org-roam-directory my-org-roam-directory)
	(org-roam-file-extensions '("org" "org_archive"))
	(org-roam-db-location (expand-file-name "org-roam.db" my-org-roam-directory))
  :bind (:map org-roam-mode-map
              (("C-c n l" . org-roam)
               ("C-c n f" . org-roam-find-file)
               ("C-c n g" . org-roam-graph))
              :map org-mode-map
              (("C-c n i" . org-roam-insert))
              (("C-c n I" . org-roam-insert-immediate)))
	:config
	(require 'org-roam-protocol)
	(setq org-roam-tag-sources '(prop vanilla)
				org-roam-index-file (expand-file-name "index.org" org-roam-directory))

	;; The following adds a hierarchy view to org-roam-node-display-template
	;; Taken from: https://github.com/org-roam/org-roam/wiki/User-contributed-Tricks#showing-node-hierarchy
	(cl-defmethod org-roam-node-hierarchy ((node org-roam-node))
		(let ((level (org-roam-node-level node)))
			(concat
			 (when (> level 0) (concat (org-roam-node-file-title node) " > "))
			 (when (> level 1) (concat (string-join (org-roam-node-olp node) " > ") " > "))
			 (org-roam-node-title node))))

	;; Setting the display of org-roam-node-find
	(setq org-roam-node-template-prefixes '(("tags" . "#")("todo" . ""))
				org-roam-node-display-template
				(concat
				 "${todo:10} "
				 ;; "${title:*} "
				 "${hierarchy:*} "			 ; provided by org-roam-node-hierarchy
				 (propertize "${tags:40}" 'face 'org-tag)) ; add tags to org-roam-node-find
				)

	;; ;; Make completion case-insensitive (https://emacs.stackexchange.com/a/77296/12336)
	;;(defun case-insensitive-org-roam-node-read (orig-fn &rest args)
	;; (let ((completion-ignore-case t))
	;; (apply orig-fn args)))
	;; (advice-add 'org-roam-node-read :around #'case-insensitive-org-roam-node-read)
	)

Different org-roam databases (“Zettelkästen”) can be maintained using directory-local settings. See the advice here: https://www.orgroam.com/manual.html#How-do-I-have-more-than-one-Org_002droam-directory_003f

((nil . ((org-roam-directory . "/path/to/alt/org-roam-dir")
         (org-roam-db-location . "/path/to/alt/org-roam-dir/org-roam.db"))))

Note that the function eval has to be used in order to evaluate elisp code:

((nil . ((eval . (setq-local
                  org-roam-directory (expand-file-name "zettel" (locate-dominating-file
                                                        default-directory ".dir-locals.el") )))
         (eval . (setq-local
                  org-roam-db-location (expand-file-name "org-roam.db"
                                                         org-roam-directory))))))

Full-text search

The org-roam manual recommends to use Deft to perform full-text search: https://www.orgroam.com/manual.html#Full_002dtext-search-with-Deft

Although I clearly prefer helm-org-rifle using the following function, it becomes quite slow with many files:

(defun tl/search-in-org-roam-files ()
	(interactive)
	(helm-org-rifle-files (org-roam-list-files)))

Eventually, I use ripgrep for the sake of speed. This is added to hydra-org-roam.

Archiving

  • State “TODO” from [2023-03-24 Fri 22:49]

In org-roam, you most probably end up either with a large number of small files, or with a small number of large files. Maintaining large files in org-roam is disadvantageous, because DB update takes longer and the long tail of unchanged headings with an ID will litter org-roam-find-file.

Since internal archiving (by adding the tag ARCHIVE) is not recognized in org-roam, I decided using external archiving to keep working files lean.

Plan

  • [ ] Optionally add archive files to search tools like helm-rifle

org-roam-protocol

https://www.orgroam.com/manual.html#Org_002droam-Protocol

org-roam-protocol requires few extra steps in order to properly work:

cat > "${HOME}/.local/share/applications/org-protocol.desktop" << EOF
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
EOF
update-desktop-database ~/.local/share/applications/
xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol

org-roam-capture-templates

https://www.orgroam.com/manual.html#The-Templating-System

(setq org-roam-capture-templates
			'(("d" "default" plain "%?"
				 :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
														":PROPERTIES:\n:ROAM_ALIASES: %^{Aliases| }\n:END:\n#+TITLE: ${title}\n#+FILETAGS: %^{Tags|:topic:}\n#+CREATED: %U\n#+LAST_MODIFIED: \n\n- links :: \n\n")
				 :unnarrowed t)
				("t" "text" plain "%?"
				 :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
														":PROPERTIES:\n:ROAM_ALIASES: %^{Aliases| }\n:END:#+TITLE: ${title}\n#+FILETAGS: :text:\n#+ROAM_ALIASES: \n#+CREATED: %U\n#+LAST_MODIFIED: \n\n- links :: \n- PDF :: \n\n")
				 :unnarrowed t)))

org-roam-dailies

https://www.orgroam.com/manual.html#Daily_002dnotes

Org-roam provides journaling capabilities akin to org-journal with org-roam-dailies.

Issues

  • [ ] I actually prefer to put all days into one file, but this does not seem to allow for modifying the body of an entry, unlike the one-file-per-day approach.
  • [ ] %? (After completing the template, position cursor here.) does not work inside the template.
(setq org-roam-dailies-directory "daily/")

(setq org-roam-dailies-capture-templates
      `(
				("f" "file" entry
				 "* %?"
				 :target (file+head "day_%<%Y-%m-%d>.org" 
														"#+title: Day %<%Y-%m-%d %A>
#+filetags: :day:
%u

Plan & Todos

Ideas & thoughts

Observations & encounters

"))
				;; ("n" "node" entry
        ;;  "* %?"
        ;;  :target (file+olp "dailies.org"
				;; 									 (,(format-time-string "%Y")
				;; 										,(format-time-string "%Y-%m %B")
				;; 										,(format-time-string "%Y-%m-%d %A"))
				;; 									 ))
				;; ;; :empty-lines 1									; throws "peculiar error"
				;; ;; :prepend t											; throws "peculiar error"
				))

org-roam-ui

https://github.com/org-roam/org-roam-ui

Graphical interface to org-roam v2 notes in the web browser. Successor of org-roam-server.

(use-package org-roam-ui
	:ensure t
  :after org-roam
	;;         normally we'd recommend hooking orui after org-roam, but since org-roam does not have
	;;         a hookable mode anymore, you're advised to pick something yourself
	;;         if you don't care about startup time, use
	;; :hook (after-init . org-roam-ui-mode)
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t 		; Whether to open your default browser when ‘org-roam-ui’ launces.
))

org-roam-bibtex

https://github.com/org-roam/org-roam-bibtex

Enables a tight integration of org-roam with helm-bibtex and org-ref.

(use-package org-roam-bibtex
	:ensure t
  :pin MELPA
  :after (org-roam org-ref)
	;; :hook (org-roam-mode . org-roam-bibtex-mode) ; will start org-roam-bibtex too late
	:config
	(require 'org-ref)										; optional: if using Org-ref v2 or v3 citation links
	(setq orb-roam-ref-format 'org-ref-v2)
 (org-roam-bibtex-mode t) 							; enable org-roam-bibtex-mode
	:custom
	(orb-insert-interface 'helm-bibtex))

Templates for org-roam-bibtex:

(add-to-list 'org-roam-capture-templates
						 '("r" "bibliography reference" plain "%?"
							 :target
							 (file+head "${citekey}.org"
													"#+TITLE: ${citekey}: ${title}\n#+FILETAGS: %^{Tags|:text:}\n#+CREATED: %U\n#+LAST_MODIFIED: \n\n- links :: \n- PDF ::\n\n")
							 :unnarrowed t))

;; (setq orb-templates
;;       '(("r" "ref" plain (function org-roam-capture--get-point) ""
;;          :file-name "${citekey}"
;;          :head "#+TITLE: ${citekey}: ${title}\n#+CREATED: %U\n#+ROAM_KEY: ${ref}\n#+ROAM_TAGS: %^{Tags} \n#+ROAM_ALIAS: \n#+LAST_MODIFIED: \n\n- links :: \n\n%?"
;;          :unnarrowed t)))

org-show

Simple interactive presentations from within Emacs. See https://github.com/jkitchin/jmax/blob/master/org/org-show.org for more information.

(require 'org-show) 

org-timeblock

https://github.com/ichernyshovvv/org-timeblock

Show events in the agenda as timeblocks.

(use-package org-timeblock
	:ensure t)

org-transclusion

https://github.com/nobiot/org-transclusion

Manual: https://nobiot.github.io/org-transclusion/

Include content from one file into another by reference.

(use-package org-transclusion
  :after org
	:ensure t
	:pin "GNU ELPA")

org-xournalpp

https://gitlab.com/vherrmann/org-xournalpp

Xournal++ is a versatile software to create hand-written notes and drawings, also allowing for the annotation of PDFs.

org-xournalpp creates a new org link type called xournalpp that:

  • when clicked on, opens xournalpp for editing the file linked, and
  • shows the updated image preview inline if org-xournalpp-mode is enabled.

Note that the installation requires quelpa.

(use-package org-xournalpp
  :ensure t
  :quelpa (org-xournalpp :fetcher gitlab :repo "vherrmann/org-xournalpp" :files ("*.el" "resources"))
  :config
  (add-hook 'org-mode-hook 'org-xournalpp-mode))

PDF interaction

interleave

https://github.com/rudolfochrist/interleave

Refile

Following this excellent blog post: https://blog.aaronbieber.com/2017/03/19/organizing-notes-with-refile.html

(setq org-refile-targets '((org-agenda-files :maxlevel . 3))
			org-refile-use-outline-path 'file
			org-outline-path-complete-in-steps nil
			org-refile-allow-creating-parent-nodes 'confirm)

Search

org-ql

https://github.com/alphapapa/org-ql

Search Org files with a powerful query language.

(use-package org-ql
  :quelpa (org-ql :fetcher github :repo "alphapapa/org-ql"
            :files (:defaults (:exclude "helm-org-ql.el"))))

Helm support is available separately.

(use-package helm-org-ql
  :quelpa (helm-org-ql :fetcher github :repo "alphapapa/org-ql"
                       :files ("helm-org-ql.el")))

The most important use case for me is performing garbage collection on my agenda files. This is how DONE headings with clocks older than 60 days are searched for:

(org-ql-search (org-agenda-files) "todo:DONE and clocked:to=-60")	        ; Non-sexp syntax
(org-ql-search (org-agenda-files) '(and (todo "DONE") (clocked :to -60))) ; Sexp syntax

Here is more complex example for searching headings with an old clock that are DONE or events not in projects:

(org-ql-search (org-agenda-files)
	'(and (and (or (todo "DONE")
								 ;; tag hierarchies are not yet supported: https://github.com/alphapapa/org-ql/issues/145 
								 (tags "event" "appointment") 
								 )
						 (not (ancestors (tags "project"))))
				(not (clocked :from -60))))

Spell checking

;; ispell
(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\|SETTINGS\\):" . ":END:"))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_EXPORT" . "#\\+END_EXPORT"))
;; flyspell
;; http://emacs.stackexchange.com/a/9347/12336
;; NO spell check for embedded snippets
(defadvice org-mode-flyspell-verify (after org-mode-flyspell-verify-hack activate)
  (let ((rlt ad-return-value)
        (begin-regexp "^[ \t]*#\\+begin_\\(src\\|html\\|latex\\|export\\)")
        (end-regexp "^[ \t]*#\\+end_\\(src\\|html\\|latex\\|export\\)")
        old-flag
        b e)
    (when ad-return-value
      (save-excursion
        (setq old-flag case-fold-search)
        (setq case-fold-search t)
        (setq b (re-search-backward begin-regexp nil t))
        (if b (setq e (re-search-forward end-regexp nil t)))
        (setq case-fold-search old-flag))
      (if (and b e (< (point) e)) (setq rlt nil)))
    (setq ad-return-value rlt)))

Structure editing

bjm/org-headline-to-top is a function that moves the current heading to the top of a section; taken from http://pragmaticemacs.com/emacs/reorder-todo-items-in-your-org-mode-agenda/.

(defun bjm/org-headline-to-top ()
  "Move the current org headline to the top of its section"
  (interactive)
  ;; check if we are at the top level
  (let ((lvl (org-current-level)))
    (cond
     ;; above all headlines so nothing to do
     ((not lvl)
      (message "No headline to move"))
     ((= lvl 1)
      ;; if at top level move current tree to go above first headline
      (org-cut-subtree)
      (beginning-of-buffer)
      ;; test if point is now at the first headline and if not then
      ;; move to the first headline
      (unless (looking-at-p "*")
        (org-next-visible-heading 1))
      (org-paste-subtree))
     ((> lvl 1)
      ;; if not at top level then get position of headline level above
      ;; current section and refile to that position. Inspired by
      ;; https://gist.github.com/alphapapa/2cd1f1fc6accff01fec06946844ef5a5
      (let* ((org-reverse-note-order t)
             (pos (save-excursion
                    (outline-up-heading 1)
                    (point)))
             (filename (buffer-file-name))
             (rfloc (list nil filename nil pos)))
        (org-refile nil nil rfloc))))))

Tables

Activate org-table-header-line-mode by default:

(setq org-table-header-line-p t)

Some functions to select and copy columns (http://emacs.stackexchange.com/a/28298/12336)

(defun org-table-goto-col-beginning ()
  "Go to beginning of current column and return `point'."
  (interactive)
  (assert (org-table-p) "Not in org-table.")
  (org-table-align)
  (let ((col (org-table-current-column)))
    (goto-char (org-table-begin))
    (org-table-goto-column col))
  (point))

(defun org-table-col-beginning ()
  "Return beginning position of current column."
  (save-excursion
    (org-table-goto-col-beginning)))

(defun org-table-goto-col-end ()
  "Goto end of current column and return `point'."
  (interactive)
  (assert (org-table-p) "Not in org-table.")
  (org-table-align)
  (let ((col (org-table-current-column)))
    (goto-char (1- (org-table-end)))
    (org-table-goto-column col)
    (skip-chars-forward "^|"))
  (point))

(defun org-table-col-end ()
  "Return end position of current column."
  (save-excursion
    (org-table-goto-col-end)))

(defun org-table-select-col ()
  "Select current column."
  (interactive)
  (set-mark (org-table-col-beginning))
  (org-table-goto-col-end))

(defun org-table-copy-col ()
  "Copy current column."
  (interactive)
  (save-excursion
    (org-table-copy-region (org-table-goto-col-beginning)
													 (org-table-goto-col-end))))

A function for copying the content of a cell:

(defun tl/org-table-copy-cell-content ()
	(interactive)
	(require 's)
	(if (org-table-p)
			(let ((cell-content (s-trim (substring-no-properties (org-table-get-field))))) 
				(kill-new cell-content)
				(message "Copied cell content to kill ring."))
		(message "Not inside org-mode table.")))

A function for killing the content of a cell:

(defun tl/org-table-kill-cell-content ()
	(interactive)
	(tl/org-table-copy-cell-content)
	(org-table-blank-field))

A function to automatically number rows:

(defun tl/org-table-number-rows ()
	"Number rows in an org-mode table by replacing the cells of the first column with consecutive numbers."
	(interactive)
	(when (org-table-p)
		(save-excursion
			(org-edit-special)
			(beginning-of-buffer)
			(when (not
						 (or (search-forward "$1=@#-1" nil t)
								 (search-forward "$1 = @#-1" nil t)))
				(move-end-of-line nil)
				(newline)
				(insert "$1=@#-1"))
			(org-table-fedit-finish)
			(org-table-recalculate 'iterate))))

valign

https://github.com/casouri/valign

Pixel-perfect visual alignment for Org and Markdown tables.

(use-package valign
	:ensure t
	;; :config
	;; (add-hook 'org-mode-hook #'valign-mode)
	)

Tags

Tags can be set globally as default using the variable org-tag-alist. Tags can be set globally and permantly using the variable org-tag-persistent-alist.

(setq org-tag-persistent-alist
			'(
				
				;; ontology
				
				(:startgroup . nil)
				("topic" . ?t)									; for headings that denote topics
				(:endgroup . nil)				

				(:startgroup . nil)
				("project" . ?p)
				(:grouptags . nil)							; kinds of projects
				("task" . nil)
				(:endgroup . nil)
				
				(:startgroup . nil)
				("event" . nil)
				(:grouptags . nil)							; kinds of events
				("appointment" . ?a)
				("conference" . nil)
				("talk" . nil)									
				("workshop" . nil)
				("session" . nil)
				(:endgroup . nil)

				(:startgroup . nil)
				("media" . ?m)
				(:grouptags . nil)							; kinds of content
				("text" . ?x)
				("video" . nil)
				("talk" . nil)
				("poster" . nil)																		
				(:endgroup . nil)

				(:startgroup . nil)
				("ressource" . ?r)
				(:grouptags . nil)							; kinds of content
				("data" . ?d)
				("corpus" . nil)
				("software" . ?s)
				(:endgroup . nil)

				;; topics
				
				(:startgroup . nil)
				("@work" . ?w)
				(:grouptags . nil)
				("administration" . ?v)
				("research" . ?r)
				("teaching" . ?e)
				(:endgroup . nil)
				
				))

Tags that should not be inherited by children:

(setq org-tags-exclude-from-inheritance '("topic" "event" "appointment" "conference" "talk" "workshop" "project" "text" "media" "video" "poster"))

Key bindings:

(define-key org-mode-map (kbd "C-c :") 'org-set-tags-command)

Timestamps/Dates

  • State “TODO” from [2021-09-09 Thu 22:28]

General

  • State “TODO” from [2021-09-09 Thu 22:28]

Insert timestamp or change type depending on region or point:

(defun tl/org-timestamp-dwim ()
	(interactive)
	(cond
	 ((use-region-p)
		(tl/org-read-date-and-insert-timestamp))
	 ((eq (org-at-timestamp-p 'inactive) 'bracket)
		(org-toggle-timestamp-type))
	 (t
		(org-time-stamp nil))))

(define-key org-mode-map (kbd "C-c .") 'tl/org-timestamp-dwim)

(defun tl/org-read-date-and-insert-timestamp ()
	"Replace date in region with active timestamp"
	(interactive)
	(if (use-region-p)
			(let ((date (org-read-date nil nil (buffer-substring-no-properties (region-beginning) (region-end)))))
				(save-excursion
					(delete-region (region-beginning) (region-end))
					(goto-char (region-beginning))
					(insert "<" date ">")
					(org-ctrl-c-ctrl-c)))
		(message "No region selected")))

Set timestamp language to English:

(setq system-time-locale "C")

Use Alt-Shift+cursor key chords to quickly change timestamps:

  • [ ] reorganize with cond
  • [ ] fix: does not work with inactive time stamps
(defun tl/org-alt-shift-up ()
	(interactive)
	(if (org-at-timestamp-p)
			(org-timestamp-up)
		(org-shiftmetaup)
		))

(defun tl/org-alt-shift-down ()
	(interactive)
	(if (org-at-timestamp-p)
			(org-timestamp-down)
		(org-shiftmetadown)
		))

(defun tl/org-alt-shift-left ()
	(interactive)
	(if (org-at-timestamp-p)
			(org-timestamp-down-day)
		(org-shiftmetaleft)
		))

(defun tl/org-alt-shift-right ()
	(interactive)
	(if (org-at-timestamp-p)
			(org-timestamp-up-day)
		(org-shiftmetaright)
		))

(defun tl/org-agenda-alt-shift-up ()
	(interactive)
	(unless (ignore-errors
						(funcall-interactively 'org-agenda-date-earlier-hours 1))
		(org-clock-convenience-timestamp-up)
		))

(defun tl/org-agenda-alt-shift-down ()
	(interactive)
	(unless (ignore-errors
						(funcall-interactively 'org-agenda-date-later-hours 1))
		(org-clock-convenience-timestamp-down)
		))

(with-eval-after-load 'org
	(define-key org-mode-map (kbd "M-S-<up>") 'tl/org-alt-shift-up)
	(define-key org-mode-map (kbd "M-S-<down>") 'tl/org-alt-shift-down)
	(define-key org-mode-map (kbd "M-S-<left>") 'tl/org-alt-shift-left)
	(define-key org-mode-map (kbd "M-S-<right>") 'tl/org-alt-shift-right)
	(define-key org-agenda-mode-map (kbd "M-S-<up>") 'tl/org-agenda-alt-shift-up)
	(define-key org-agenda-mode-map (kbd "M-S-<down>") 'tl/org-agenda-alt-shift-down)
	(define-key org-agenda-mode-map (kbd "M-S-<left>") 'org-agenda-date-earlier)
	(define-key org-agenda-mode-map (kbd "M-S-<right>") 'org-agenda-date-later)
	)

CREATED timstamp in properties drawer

See https://emacs.stackexchange.com/a/21302/12336

(defvar org-created-property-name "CREATED"
  "The name of the org-mode property that stores the creation date of the entry")

(defun org-set-created-property (&optional active NAME)
  "Set a property on the entry giving the creation time.

By default the property is called CREATED. If given the `NAME'
argument will be used instead. If the property already exists, it
will not be modified."
  (interactive)
  (let* ((created (or NAME org-created-property-name))
         (fmt (if active "<%s>" "[%s]"))
         (now  (format fmt (format-time-string "%Y-%m-%d %a %H:%M"))))
    (unless (org-entry-get (point) created nil)
      (org-set-property created now))))

Recurring events

http://karl-voit.at/2017/01/15/org-clone-subtree-with-time-shift/

TOC

toc-org

https://github.com/snosov1/toc-org

Insert or update a Github-compatible table of contents under a heading with tag :TOC:.

  • :TOC_2: sets the max depth of the headlines in the table of contents to 2 (the default)
  • :TOC_2_gh: sets the max depth as in above and also uses GitHub-style hrefs in the table of contents (this style is default). The other supported href style is ‘org’, which is the default org style.
(use-package toc-org
	:ensure t
	;; :config
	;; (if (require 'toc-org nil t)
	;; 		(add-hook 'org-mode-hook 'toc-org-mode)
	;; 	(warn "toc-org not found"))
	)

Todo keywords

(Must appear before org-agenda settings. Otherwise org-todo-keywords may not get updated buffer locally.)

https://orgmode.org/manual/TODO-Items.html

TODO keywords appear in headlines right after the initial stars. They can be used to keep track of the states of projects and tasks.

State keywords for tasks and projects:

  • TODO: something that needs to be done, maybe now, maybe later.
  • NEXT: something that can be done now. There’s everything you need to start doing it.
  • INPROGRESS: something that is being done right now.
  • WAITING: something that cannot be done now, because some prerequisites are not yet met.
  • HOLD: something that cannot be done now, and the prerequisites are unlikely to be fullfilled in the future.
  • SOMEDAY: you are not yet sure whether this needs to be done.
  • DELEGATED: something that has been delegated and is not tracked any more.

State keywords for events and appointments:

  • TENTATIVE: not yet confirmed that something will take place.
  • CONFIRMED: confirmed that something will take place. This is the default.
  • CANCELLED: something will not take place though it was planned.

From the org-mode documentation (http://orgmode.org/worg/doc.html#org-todo-keywords):

“WAIT(w@/!)”. “@” means to add a note (with time), “!” means to record only the time of the state change. With X and Y being either “@” or “!”, “X/Y” means use X when entering the state, and use Y when leaving the state if and only if the target state does not define X. You may omit any of the fast-selection key or X or /Y, so WAIT(w@), WAIT(w/@) and WAIT(@/@) are all valid.

(setq org-todo-keywords
      (quote ((sequence "TODO(t!)" "NEXT(n!)" "INPROGRESS(p!)" "|" "DONE(d)")
              (sequence "WAITING(w@/!)" "HOLD(h@/!)" "SOMEDAY(s)" "|" "DELEGATED(l@/!)" "CANCELLED(c@/!)")
							(sequence "TENTATIVE(T!)" "|" "CONFIRMED(C!)" "CANCELLED(c@/!)" ))))

(setq org-todo-keyword-faces
      (quote (("TODO" :foreground "red" :weight bold)
              ("NEXT" :foreground "blue" :weight bold)
              ("INPROGRESS" :foreground "gold" :weight bold)
              ("DONE" :foreground "forest green" :weight bold)
              ("WAITING" :foreground "cyan" :weight bold)
              ("HOLD" :foreground "magenta" :weight bold)
              ("CANCELLED" :foreground "forest green" :weight bold)
              ("DELEGATED" :foreground "forest green" :weight bold)
							("SOMEDAY" :foreground "goldenrod" :weight bold)
							("CONFIRMED" :foreground "forest green" :weight bold)
							("TENTATIVE" :foreground "cyan" :weight bold)
							)))

Fast TODO selection:

(setq org-use-fast-todo-selection t)

Insert timestamp when task is marked as DONE:

(setq org-log-done 'time)

Insert timestamp when changing deadline:

(setq org-log-redeadline 'time)

Do not insert timestamp or note when changing schedule:

(setq org-log-reschedule nil)

As I frequently change SCHEDULED and (mis)use it for planning when to work next on a project, this is to prevent the LOGBOOK from being cluttered.

Observe dependencies between TODOs:

(setq org-enforce-todo-dependencies t)

Last modified

If the file keyword #+LAST_MODIFIED is present within the first 20 lines, the following timestamp gets updated when saving the file:

(add-hook 'org-mode-hook (lambda ()
													 (set (make-local-variable 'time-stamp-pattern)
																"20/^#\\+LAST_MODIFIED:[ \t]+%:y-%02m-%02d %3a %02H:%02M:%02S$")))  
(add-hook 'before-save-hook 'time-stamp)

Accounting

Ledger

https://github.com/ledger/ledger-mode

Ledger is a powerful, double-entry accounting system.

(use-package ledger-mode
	:ensure t
  :mode ("\\.ledger\\'" "\\.dat\\'")
  :config
  (define-key ledger-mode-map (kbd "C-c c") 'ledger-mode-clean-buffer)
  (define-key ledger-mode-map (kbd "C-<tab>") nil)
  (setq ledger-post-amount-alignment-at :decimal
        ledger-post-amount-alignment-column 49
				ledger-default-date-format "%Y-%m-%d"
				ledger-use-iso-dates t
				ledger-reconcile-default-commodity ""
        ledger-clear-whole-transactions t)
  (use-package flycheck-ledger
		:ensure t))

Calendar/Diary & address book

Address book

vdirel

https://github.com/DamienCassou/vdirel/

vidirel reads and manipulates contacts in the vdir format.

Issues:

  • [ ] Import of contacts crashes with error: “Wrong number of arguments: #<subr org-vcard-import-parse>, 2”
(use-package vdirel
	:ensure t
	:pin MELPA
	:config
	(setq vdirel-repositories (file-expand-wildcards "~/PATH/TO/VDIRs")))

khardel

https://github.com/DamienCassou/khardel

Uses khard and vdirsyncer to edit and sync local copies of CarDAV address books. Also supports org-mode links to khard entries.

(use-package khardel
	:ensure t
	:config
	(require 'khardel-org) 								; org links to khard contact edit buffers
)

Function to sync contacts using vdirsyncer:

(defun tl/vdirsyncer-sync-contacts ()
	"Start vdirsyncer in a subprocess in order to sync contacts."
	(interactive)
	(let* ((command "vdirsyncer-sync-contacts.sh")
				 (name "*vdirsyncer-sync*")
				 (buffer (progn (when (get-buffer name)
													(kill-buffer name))
												(get-buffer-create name))))
		(with-current-buffer buffer
			(call-process "vdirsyncer-sync-contacts.sh" nil t nil)
			(setq buffer-read-only t)
      (local-set-key (kbd "q") 'kill-this-buffer)
			(switch-to-buffer buffer)
			(goto-char (point-min)))
		;; (start-process-shell-command name buffer command)
		(message "tl/vdirsyncer-sync-contacts: Syncing in progress, see buffer %s" name)
		))

BBDB

http://savannah.nongnu.org/projects/bbdb/

Built-in address book in Emacs.

Unfortunately, the current version of BBDB is lacking a decent manual. For an outdated manual, see: https://bbdb.sourceforge.net/bbdb.html

BBDB seems to only allow for one database (file). Therefore (and for other reasons), I switched to EBDB.

(use-package bbdb
	:ensure t
	:config
	(progn
		(bbdb-initialize '(gnus message mu4e))
		;; (add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus)
		;; (add-hook 'gnus-startup-hook 'bbdb-insinuate-message)
		;; (add-hook 'message-setup-hook 'bbdb-define-all-aliases)

		(setq bbdb-file my-bbdb-file 				; Addresses are stored in one file
					bbdb-auto-revert t						; Revert DB when changing the bbdb-
					bbdb-check-auto-save-file t		; Auto-save to bbdb-file
					
					bbdb-phone-style nil					; nanp = North American Numbering Plan
					bbdb-complete-mail-allow-cycling t ; Cycle mail addresses when calling ‘bbdb-complete-mail’.
					
					;; Pop-up window
					bbdb-pop-up-window-size 0.5		; Default split is 0.5
					bbdb-pop-up-target-lines 1 ; The window should be as small as possible
					bbdb-use-pop-up nil        ; Down't show pup-up

					;; bbdb-complete-name-allow-cycling t ; Not documented
					;; bbdb-complete-name-full-completion t ; Not documented
					;; bbdb-completion-type 'primary-or-name ; Not documented
					;; bbdb-offer-save 1  ; Not documented
					;; bbdb-electric-p t	 ; Not documented
					;; bbdb-expand-mail-aliases t		; Not documented

					)
		))
bbdb-vcard

https://github.com/tohojo/bbdb-vcard

Import and export vCards to/from BBDB.

(use-package bbdb-vcard
	:pin MELPA 
  :ensure t)
helm-bbdb

https://github.com/emacs-helm/helm-bbdb

A Helm Interface to BBDB.

(use-package helm-bbdb
  :ensure t)
  (global-set-key (kbd "<f7> a") 'helm-bbdb)

EBDB

https://github.com/girzel/ebdb

An object-orientied port of bbdb.

Issues

  • [ ] Unfortunately, vcard/vcf files cannot be imported: girzel/ebdb#60
(use-package ebdb
	:ensure t
	:config
	(require 'ebdb-gnus)
	(require 'ebdb-message))

helm-khard

!!! WORK IN PROGRESS !!!

https://github.com/timmli/helm-khard

A Helm interface for khard.

(use-package uuidgen
	:ensure t
	:pin MELPA)

(use-package helm-khard
	:after mu4e
  :load-path "lisp/helm-khard/"
	:config
	(setq helm-khard-vdirsyncer-command "vdirsyncer-sync-contacts.sh"
				helm-khard-sync-after-editing t
				helm-khard-sync-during-initialization t)
	(advice-add 'mu4e--update-contacts :after #'helm-khard--inject-contacts-into-mu4e))

Built-in Calendar: general settings

https://www.gnu.org/software/emacs/manual/html_node/emacs/Calendar_002fDiary.html

(setq calendar-week-start-day 1)			; weeks start on Mondays

(setq calendar-intermonth-text
      '(propertize
        (format "%2d"
                (car
                 (calendar-iso-from-absolute
                  (calendar-absolute-from-gregorian (list month day year)))))
        'font-lock-face 'font-lock-warning-face))

(setq calendar-intermonth-header
      (propertize "W"                  ; or e.g. "KW" in Germany
                  'font-lock-face 'font-lock-keyword-face))

German holidays

https://github.com/rudolfochrist/german-holidays

Adds German holidays to the calendar.

(use-package german-holidays
  :ensure t
  :config
  (setq calendar-holidays holiday-german-holidays)
  ;; (setq calendar-holidays holiday-german-BW-holidays)
  )

iCalendar import

iCalendar files can be imported

  • via calfw
  • via the Emacs diary: icalendar-import-file imports ics-files from caldav servers into the diary file. This will eventually be shown in org-agenda based on the variable org-agenda-include-diary
  • via org-mode and mu4e

calfw

https://github.com/kiwanami/emacs-calfw/

Powerful calendar view for Emacs.

Issues:

  • [ ] Week numbers are not shown: kiwanami/emacs-calfw#137
  • [ ] Recurring events in imported ics files are not shown.
    (use-package calfw
    	:ensure t 
    	:init
    
    	(use-package calfw-org								; for org-agenda
    		:ensure t)
    	(use-package calfw-ical								; for ics files
    		:ensure t)
    	(use-package calfw-cal 								; for diaries
    		:ensure t)
    
    	(setq cfw:render-line-breaker 'cfw:render-line-breaker-simple)
    	)
        

Define a key to update calendars from within calfw:

(define-key cfw:calendar-mode-map (kbd "u") 
	#'(lambda () (interactive)
			(when (fboundp 'tl/update-my-calendars) ; tl/update-my-calendars can be redefined in private-emacs-settings-after.el
				(tl/update-my-calendars))
			(my-open-calfw)
			))

Calendars are defined in my-private-calendars:

(defvar my-private-calendars
			'((:name "NAME"
							 :url "URL"
							 :color "Orange"
							 :description ""))
	"This variable stores the remote calendars as a list of plists.

A calendar consists of a list of KEY VALUE pairs (plist) with keys :name, :url, :color, and :description.
It is recommended to populate this variable in `private-emacs-settings-after.el`.")

Helper functions:

(require 'dash)
(defun tl/update-my-calendars ()
	(let ((newpcal
				 (-zip-with
					(lambda (s1 s2) (append s1 s2))
					(mapcar
					 (lambda (cal)
						 (let* ((name (plist-get cal ':name))
										(url (plist-get cal ':url))
										(color (plist-get cal ':color))
										(file (if (plist-get cal ':file)
															(plist-get cal ':file)
														(concat trimmed-private-emacs-settings-dir name ".ics"))))
							 (tl/check-url-and-download-file url file)
							 `(:file ,file)))
					 my-private-calendars)
					my-private-calendars)
				 ))
		(setq my-private-calendars newpcal)))

(defun my-open-calfw ()
	(interactive)
	(cfw:open-calendar-buffer
	 :contents-sources
	 (append 
		(list
		 (cfw:org-create-source "Orange")							; orgmode source
		 ;; (cfw:cal-create-source)											; diary source
		 )
		(mapcar
		 (lambda (cal)
			 (let* ((name (plist-get cal ':name))
							(url (plist-get cal ':url))
							(color (plist-get cal ':color))
							(file (if (plist-get cal ':file)
												(plist-get cal ':file)
											(concat trimmed-private-emacs-settings-dir name ".ics"))))
				 (cfw:ical-create-source name file color)))
		 my-private-calendars))
	 ))

(defun tl/check-url-and-download-file (url filename)
	(if (url-https-file-exists-p url)
			(url-copy-file url filename t nil nil)
		(display-warning 'calendar (concat "Could not download " filename))))

khalel

https://gitlab.com/hperrey/khalel

Uses khal and vdirsyncer to sync local copies of CalDAV calendars with calendar entries in org-mode files. As for address books, please have a look at khard and khardel.

Important limitations:

  • Only for importing calendars as org-mode files. Hence, changes to calendars must be made upstream with a client such as khal.
+-------+    +----------+    +-------+    +-------+    +---------+
|       |--->|          |--->|       |--->|       |--->|         |
| remote|    |vdirsyncer|    |  khal |    |khalel |    | org mode|
|       |<===|          |<===|       |===>|       |===>|         |
+-------+    +----------+    +-------+    +-------+    +---------+
                                 ^
                                 :
                                 :
             +----------+    +--------+
             |          |    |        |       --> existing events
             | org mode |===>| khalel |
             |          |    |        |       ==> new events
             +----------+    +--------+

org-ical-sync

!!! WORK IN PROGRESS !!!

https://codeberg.org/timmli/org-ical-sync

Sync Org headings with iCal database.

(use-package org-ical-sync
	:after org
  :load-path "lisp/org-ical-sync/"
	:config
	(org-ical-sync-setup)
	(setq org-ical-sync-vdirsyncer-command "vdirsyncer-sync-calendars.sh"
				org-ical-sync-after-save t)
	(org-ical-sync-call-vdirsyncer-now))

Coding & Writing

Artist mode

http://www.lysator.liu.se/~tab/artist/ http://dinasis.com/oliver/Learning_GNU_Emacs/gnu3-CHP-7-SECT-6.html

Artist mode is a built-in minor mode for picture mode in Emacs with which simple geometric forms (lines, rectangles, circles, ellipses, …) can be drawn using just text characters. This can be handy when wanting to create, e.g., simple diagrams to be included in your code.

Here is an example:

                                             
                              -------        
                           --/       \--     
+---------+               /             \    
|         |              /               \   
|  Hello  | <--------->  |    World!     |   
|         |              \               /   
+---------+               \             /    
                           --\       /--     
                              -------        
                                             

I have made a Hydra for artist-mode.

Note that the function set of artist-mode is very basic. For example, one cannot resize a rectangular directly.

Clojure

clojure-mode

https://github.com/clojure-emacs/clojure-mode/

Font-lock (syntax highlighting), indentation, navigation and refactoring support for Clojure(Script).

(use-package clojure-mode
  :ensure t
  :mode (("\\.clj\\'" . clojure-mode)
         ("\\.edn\\'" . clojure-mode))
	:init
	(add-hook 'clojure-mode-hook #'subword-mode))

clj-refactor

https://github.com/clojure-emacs/clj-refactor.el

Powerful refactoring functionality for Clojure projects which complements clojure-mode and CIDER.

(use-package clj-refactor
  :defer t
  :ensure t
  :diminish clj-refactor-mode
  :config (cljr-add-keybindings-with-prefix "C-c C-m"))

cider

https://github.com/clojure-emacs/cider

CIDER is the Clojure(Script) Interactive Development Environment that Rocks! AKA REPL. A basic configuration of Cider is shown here: http://ccann.github.io/2015/10/18/cider.html

(use-package cider
  :ensure t
  :defer t
  :init (add-hook 'cider-mode-hook #'clj-refactor-mode)
  :diminish subword-mode
	:bind (:map cider-repl-mode-map
							("C-h a" . cider-apropos)
							:map clojure-mode-map
							("C-h a" . cider-apropos))
  :config
  (setq nrepl-log-messages t									 ; useful for debugging
        cider-repl-display-in-current-window t ; switch to REPL in this window
        cider-repl-use-clojure-font-lock t ; syntax highlighting in REPL
        cider-prompt-save-file-on-load 'always-save	; just always save when loading buffer
        cider-font-lock-dynamically '(macro core function var) ; syntax highlight all namespaces
        nrepl-hide-special-buffers t		; hide nrepl buffers from menu
        cider-overlays-use-font-lock t)	; syntax highlight evaluation overlays
  (cider-repl-toggle-pretty-printing)) ; REPL always pretty-prints results

cider-eval-sexp-fu

https://github.com/clojure-emacs/cider-eval-sexp-fu

From the README: ”eval-sexp-fu provides tiny improvements to expression evaluation - e.g. the expression you’ve just evaluated would briefly flash and so on.”

(use-package cider-eval-sexp-fu
	:ensure t
  :defer t)

EditorConfig

EditorConfig helps specifying and sharing coding styles for consistent coding. Coding styles are stored in a file named .editorconfig that is located in the directory of the opened file or in some parent directory.

Here is an example from the website:

# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8

# 4 space indentation
[*.py]
indent_style = space
indent_size = 4

# Tab indentation (no size specified)
[Makefile]
indent_style = tab

# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2

# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

editorconfig-emacs

https://github.com/editorconfig/editorconfig-emacs

EditorConfig plugin for Emacs.

(use-package editorconfig
  :ensure t
  :config
  (editorconfig-mode 1))

Elisp

Code styling & package writing

Guides

Makefiles

Benchmarking

elisp-benchmarks

https://elpa.gnu.org/packages/elisp-benchmarks.html

benchmark (built-in)

See http://xahlee.info/emacs/emacs/elisp_benchmark.html for examples.

Usage:

  • (benchmark-run REPETITIONS FORMS)
  • (benchmark-run-compiled REPETITIONS FORMS): Use the ad-hoc byte-compiled version of FORMS.
profiler (built-in)

https://www.gnu.org/software/emacs/manual/html_node/elisp/Profiling.html

Interactive profiler that records the CPU time consumed by function calls.

Usage:

  • (profiler-start MODE): start profiler
  • (profiler-stop): end profiler
  • (profiler-report): show profiler report

Eldev

https://github.com/emacs-eldev/eldev

Elisp development tool similar to Cask, but fully written in Elisp.

Last modified

If the file keyword ;; Last modified: is present within the first 20 lines, the following timestamp gets updated when saving the file:

(add-hook 'emacs-lisp-mode-hook (lambda ()
													 (set (make-local-variable 'time-stamp-pattern)
																"20/^;; Last modified:[ \t]+%:y-%02m-%02d %3a %02H:%02M:%02S$")))  
(add-hook 'before-save-hook 'time-stamp)

Gnuplot

gnuplot

https://github.com/emacsorphanage/gnuplot

Major mode for gnuplot files.

(use-package gnuplot
	:ensure t
	:mode	"\\.\\(gp\\|gnuplot|plt\\)$"
	)

Example:

f(x) = x**2
plot f(x)

Graphviz/dot

https://github.com/ppareit/graphviz-dot-mode

Major mode for graphiz/dot code.

(use-package graphviz-dot-mode
  :ensure t
	:mode "\\.dot\\'")

HTML

htmlize

https://github.com/hniksic/emacs-htmlize

Convert buffers and files to HTML while preserving text and formatting.

(use-package htmlize
	:ensure t)

JavaScript

js2-mode

https://github.com/mooz/js2-mode

Major mode for JavaScript files.

(use-package js2-mode
	:ensure t
	:mode
	("\\.js\\'" . js2-mode)
	:config
	;; Better imenu
	(add-hook 'js2-mode-hook #'js2-imenu-extras-mode)
)

js2-refactor

https://github.com/js-emacs/js2-refactor.el

(use-package js2-refactor
	:ensure t
	:after js2-mode
	:config
	(add-hook 'js2-mode-hook #'js2-refactor-mode)
	(js2r-add-keybindings-with-prefix "C-c C-r")
	(define-key js2-mode-map (kbd "C-k") #'js2r-kill))

Last modified

If the file keyword // Last modified: is present within the first 20 lines, the following timestamp gets updated when saving the file:

(add-hook 'js-mode-hook (lambda ()
                          (set (make-local-variable 'time-stamp-pattern)
                               "20/^// Last modified:[ \t]+%:y-%02m-%02d %3a %02H:%02M:%02S$")))
(add-hook 'before-save-hook 'time-stamp)

JSDoc

JSDoc is a commeting style of JavaScript source code. Other than docstrings in languages like Lisp, however, these comments are not part of the program, and are usually stripped off during parsing.

jsdoc.el

https://github.com/isamert/jsdoc.el

Provides simple JSDoc support for the prize of requiring Tree-Sitter.

(use-package jsdoc
:ensure t)

indium

https://github.com/NicolasPetton/Indium

JavaScript development environment for Emacs

tern

for better autocompletion

LaTeX

AUCTeX

  • [ ] How to do completion of custom macros?
(use-package tex
	:ensure auctex												; because auctex overwrites tex
	:mode ("\\.bbx\\'" . latex-mode)("\\.cbx\\'" . latex-mode)
	:init

	;; to activate auctex
	(setq TeX-auto-save t)  
	(setq TeX-auto-local
				(expand-file-name "temp" user-emacs-directory))
	(setq TeX-parse-self t)
	(setq-default TeX-master nil)
	(setq TeX-save-query nil) ; autosave before compiling 

	;; Show compilation log
	(setq TeX-show-compilation nil) ; always show and follow TeX output
	;; FIXME: Make C-c C-l behave like this
	(setq compilation-scroll-output t)

	;; don't indent
	(setq LaTeX-indent-level 0)
	(setq LaTeX-item-indent 0)

	;; viewer
	(setq TeX-PDF-mode t)
	(setq TeX-source-correlate-mode t)
	(setq TeX-source-correlate-method 'synctex)

	(when (eq system-type 'windows-nt) 
		(setq TeX-view-program-list
					'(("Sumatra PDF" ("\"SumatraPDF.exe\" -reuse-instance"
														(mode-io-correlate " -forward-search %b %n ") " %o"))))
		(eval-after-load 'tex
			'(progn
				 (assq-delete-all 'output-pdf TeX-view-program-selection)
				 (add-to-list 'TeX-view-program-selection '(output-pdf "Sumatra PDF"))))
		)

	(when (not (eq system-type 'windows-nt))
		;; Use pdf-tools to open PDF files
		(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
					TeX-source-correlate-start-server t)

		;; Update PDF buffers after successful LaTeX runs
		(add-hook 'TeX-after-compilation-finished-functions
							#'TeX-revert-document-buffer)
		)
	
	;; set up engines
	(setq TeX-engine-alist 
				'(
					(xetex "XeTeX" 
								 "xetex --file-line-error" 
								 "xelatex --file-line-error" 
								 "xetex")
					(xetex_sh "XeTeX shell escape" 
										"xetex --file-line-error --shell-escape" 
										"xelatex --file-line-error --shell-escape" 
										"xetex")))

	;; make LaTeXmk default
	(use-package auctex-latexmk
		:ensure t
		:config
		(auctex-latexmk-setup)
		(setq auctex-latexmk-inherit-TeX-PDF-mode t)
		(setq TeX-command-force "LatexMk")  ; remember to set path variable accordingly!
		)
	
	:config
	;; font keys
	(defun TeX-italic()
		(interactive)
		(TeX-font nil ?\C-i))
	(defun TeX-bold()
		(interactive)
		(TeX-font nil ?\C-b))
	(defun TeX-typewriter()
		(interactive)
		(TeX-font nil ?\C-t))
	(defun TeX-emphasis()
		(interactive)
		(TeX-font nil ?\C-e))
	(defun TeX-smallcaps()
		(interactive)
		(TeX-font nil ?\C-c))
	(defun TeX-italic-replace()
		(interactive)
		(TeX-font t ?\C-i))
	(defun TeX-bold-replace()
		(interactive)
		(TeX-font t ?\C-b))
	(defun TeX-typewriter-replace()
		(interactive)
		(TeX-font t ?\C-t))
	(defun TeX-emphasis-replace()
		(interactive)
		(TeX-font t ?\C-e))
	(defun TeX-smallcaps-replace()
		(interactive)
		(TeX-font t ?\C-c))
	(defun TeX-deletefont()
		(interactive)
		(TeX-font nil ?\C-d))
	(define-key LaTeX-mode-map (kbd "C-c C-f i") 'TeX-italic)
	(define-key LaTeX-mode-map (kbd "C-c C-f b") 'TeX-bold)
	(define-key LaTeX-mode-map (kbd "C-c C-f t") 'TeX-typewriter)
	(define-key LaTeX-mode-map (kbd "C-c C-f e") 'TeX-emphasis)
	(define-key LaTeX-mode-map (kbd "C-c C-f s") 'TeX-smallcaps)
	(define-key LaTeX-mode-map (kbd "C-c C-f c") 'TeX-smallcaps)
	(define-key LaTeX-mode-map (kbd "C-c C-f d") 'TeX-deletefont)	
	(define-key LaTeX-mode-map (kbd "C-c C-f DEL") 'TeX-deletefont)
	(define-key LaTeX-mode-map (kbd "C-c f i") 'TeX-italic)
	(define-key LaTeX-mode-map (kbd "C-c f b") 'TeX-bold)
	(define-key LaTeX-mode-map (kbd "C-c f t") 'TeX-typewriter)
	(define-key LaTeX-mode-map (kbd "C-c f e") 'TeX-emphasis)
	(define-key LaTeX-mode-map (kbd "C-c f s") 'TeX-smallcaps)
	(define-key LaTeX-mode-map (kbd "C-c f c") 'TeX-smallcaps)
	(define-key LaTeX-mode-map (kbd "C-c f d") 'TeX-deletefont)
	(define-key LaTeX-mode-map (kbd "C-c f DEL") 'TeX-deletefont)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! i") 'TeX-italic-replace)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! b") 'TeX-bold-replace)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! t") 'TeX-typewriter-replace)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! e") 'TeX-emphasis-replace)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! s") 'TeX-smallcaps-replace)
	(define-key LaTeX-mode-map (kbd "C-c C-f ! c") 'TeX-smallcaps-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! i") 'TeX-italic-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! b") 'TeX-bold-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! t") 'TeX-typewriter-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! e") 'TeX-emphasis-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! s") 'TeX-smallcaps-replace)
	(define-key LaTeX-mode-map (kbd "C-c f ! c") 'TeX-smallcaps-replace)

	;; activate folding
	(add-hook 'LaTeX-mode-hook (lambda ()
															 (TeX-fold-mode 1)
															 ;; (TeX-fold-buffer)
															 ))
	;; folding behavior for environments
	(custom-set-variables '(TeX-fold-env-spec-list
													'(("[comment]" ("comment"))
														("[figure]" ("figure"))
														("[tikz]" ("tikzpicture")))))


	:bind (:map  LaTeX-mode-map
							 ;; ("C-l C-q" . align-current) ; useful command to align arrays
							 ;; ("C-l H-i" . align-current) ; useful command to align arrays							 
							 ;; keys for error browsing (disabled; see hydra settings)
							 ;; ("<f4>" . TeX-next-error)	 
							 ;; ("S-<f4>" . TeX-previous-error)
							 ;; ("C-<f4>" . TeX-error-overview)
							 ;; miscellaneous keys
							 ("C-c <backspace>" . TeX-clean)
							 ("C-<return>" . LaTeX-close-environment)
							 ;; goto keys
							 ("C-c {" . LaTeX-find-matching-begin)
							 ("C-c }" . LaTeX-find-matching-end)
							 ;; ("C-c C-l" . (lambda () (interactive) ()))  ;FIXME
							 )
	)

Autocompletion: see company.

reftex

https://www.gnu.org/software/auctex/reftex.html

  • [ ] Issue: \section, \frametitle etc. are not shown in TOC when not being line-inital or preceded by whitespaces.
  • [ ] Issue: \begin{frame} ist not recognized.
(use-package reftex
	:diminish reftex-mode
	:init
	(add-hook 'latex-mode-hook 'turn-on-reftex)
	(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
	(setq reftex-plug-into-AUCTeX t
				;; reftex-ref-style-default-list '("Cleveref" "Hyperref" "Fancyref")
				;; reftex-toc-split-windows-horizontally t
				reftex-ref-macro-prompt nil			; go straight to the labels when referencing
				reftex-bibliography-commands '("bibliography" "nobibliography" "addbibresource")
				reftex-default-bibliography (list user-bibliography-file)
				)

	;; add frametitle to TOC
	(setq reftex-section-levels '(("part" . 0)
																("chapter" . 1)
																("section" . 2)
																("subsection" . 3)
																("subsubsection" . 4)
																("frametitle" . -3)
																("paragraph" . 5)
																("subparagraph" . 6)
																("addchap" . -1)
																("addsec" . -2)))
	
	;; connect reftex to imenu
	;; (add-hook 'reftex-load-hook 'imenu-add-menubar-index) ; Error: imenu unavailable: "The mode ‘Fundamental’ does not support Imenu"
	(add-hook 'reftex-mode-hook 'imenu-add-menubar-index)

	;; isearch across files
	(add-hook 'reftex-mode-hook  (lambda () (reftex-isearch-minor-mode)))
	
	:config
	
	;; jumping around like in org-mode
	(define-key LaTeX-mode-map (kbd "C-c C-j") 'tl/reftex-in-follow-mode)
	(define-key LaTeX-mode-map (kbd "C-n") 'tl/reftex-next)
	(define-key LaTeX-mode-map (kbd "C-p") 'tl/reftex-previous)
	(defun tl/reftex-in-follow-mode()
		(interactive)
		(setq reftex-toc-follow-mode t)
		(reftex-toc))
	(defun tl/reftex-next ()
		(interactive)
		(next-line)														; no clue why this is necessary
		(tl/reftex-in-follow-mode)
		(reftex-toc-next)
		(reftex-toc-goto-line-and-hide)
		(recenter))
	(defun tl/reftex-previous ()
		(interactive)
		(next-line)														; no clue why this is necessary
		(tl/reftex-in-follow-mode)
		(reftex-toc-previous)
		(reftex-toc-goto-line-and-hide)
		(recenter))
	
	:bind (:map LaTeX-mode-map
							("C-c ]" . reftex-citation); same as in org-mode
						  ) 
	)

Autocompletion: see *company.

bibtex

http://www.jonathanleroux.org/bibtex-mode.html

(setq bibtex-dialect 'biblatex)

Align at equal sign instead of field text:

(setq bibtex-align-at-equal-sign t)

Formatting:

(setq bibtex-field-indentation 2
			bibtex-text-indentation 14
			bibtex-contline-indentation 15)
(add-hook 'bibtex-mode-hook (lambda () (set-fill-column 999))) ; don't fill column

Cleaning actions:

(setq bibtex-entry-format
			'(opts-or-alts ; delete empty optional and alternative fields
				;; required-fields ; signal an error if a required field is missing.
				numerical-fields ; delete delimiters around numeral fields
				inherit-booktitle ; insert booktitle if it is in the crossref
				realign						; realign entries
				last-comma				; add or delete last comma
				delimiters				; change delimiters 
				unify-case				; change case of entry types and field names
				sort-fields
				)) 							

Key generation:

(setq
 ;; name part
 bibtex-autokey-names 1						; maximal number of names used
 bibtex-autokey-names-stretch 1		; additionally used names, if then all names are used
 bibtex-autokey-preserve-case t		; probably deprecatedd; instead use bibtex-autokey-name-case-convert
 bibtex-autokey-name-case-convert 'identity 
 bibtex-autokey-name-separator ":"
 bibtex-autokey-additional-names ":etal"
 ;; year part
 bibtex-autokey-year-length 2 			; size of year part (from the right)
 bibtex-autokey-name-year-separator ":" 
 bibtex-autokey-year-use-crossref-entry t ; use year from crossref (depends on bibtex-maintain-sorted-entries)
 ;; title part
 bibtex-autokey-year-title-separator "-"
 bibtex-autokey-titleword-length 20	  ; max. number of used characters from title words
 bibtex-autokey-titlewords 5						; max. number of used title words
 bibtex-autokey-titleword-case-convert-function 'downcase
 )

Sorting entries:

(setq bibtex-maintain-sorted-entries t)
Import

Taken from http://www.anghyflawn.net/blog/2014/emacs-give-a-doi-get-a-bibtex-entry/:

(defun get-bibtex-from-doi (doi)
 "Get a BibTeX entry from the DOI"
 (interactive "MDOI: ")
 (let ((url-mime-accept-string "text/bibliography;style=bibtex"))
   (with-current-buffer 
     (url-retrieve-synchronously 
       (format "http://dx.doi.org/%s" 
       	(replace-regexp-in-string "http://dx.doi.org/" "" doi)))
     (switch-to-buffer (current-buffer))
     (goto-char (point-max))
     (setq bibtex-entry 
     	  (buffer-substring 
          	(string-match "@" (buffer-string))
              (point)))
     (kill-buffer (current-buffer))))
 (insert (decode-coding-string bibtex-entry 'utf-8))
 (bibtex-fill-entry))
Entry templates

Function to add timestamp:

(defun tl/bibtex-add-timestamp ()
	(interactive)
	(bibtex-set-field "timestamp" (format-time-string "%Y-%m-%d" (current-time))) 
)

Use bibtex-entry when inserting entry templates:

(define-key bibtex-mode-map (kbd "C-c C-e") 'bibtex-entry)

Documentation of bibtex-entry-field-alist:

Alist of BibTeX entry types and their associated fields. Elements are lists (ENTRY-TYPE DOC REQUIRED CROSSREF OPTIONAL). ENTRY-TYPE is the type of a BibTeX entry. DOC is a brief doc string used for menus. If nil ENTRY-TYPE is used. REQUIRED is a list of required fields. CROSSREF is a list of fields that are optional if a crossref field is present; but these fields are required otherwise. OPTIONAL is a list of optional fields.

Each element of these lists is a list of the form (FIELD COMMENT INIT ALTERNATIVE). COMMENT, INIT, and ALTERNATIVE are optional.

FIELD is the name of the field. COMMENT is the comment string that appears in the echo area. If COMMENT is nil use ‘bibtex-BibTeX-field-alist’ if possible. INIT is either the initial content of the field or a function, which is called to determine the initial content of the field. ALTERNATIVE if non-nil is an integer that numbers sets of alternatives, starting from zero.

MUSS

See https://github.com/timmli/biblatex-muss

(defvar bibtex-muss-entry-alist 
	`(("Article" "Article in a journal"
		 ;; required
		 (("author")
			("title" "Title of the article (BibTeX converts it to lowercase)"))
		 ;; required without crossref
		 (("journal")
			("year"))
		 ;; optional
		 (("volume" "Volume of the journal")
			("number" "Number of the journal (only allowed if entry contains volume)")
			("pages" "Pages in the journal")
			("url")
			("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("Book" "A book with one or more authors where the authors share credit for the work as a whole"
		 ;; required
		 (("author")
			("title")
			("year")
			("doi" nil nil 1)
			("url" nil nil 1)
			("publisher" nil nil 1)
			)
		 ;; required without crossref
		 nil
		 ;; optional
		 (("volume" "Volume of the book in the series")
			("number" "Number of the book in a small series (overwritten by volume)")
			("series" "Series in which the book appeared")
			("edition" "Edition of the book as a capitalized English word")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("Collection" "A collection with multiple, self-contained contributions by distinct authors which have their own title"
		 ;; required
		 (("editor")
			("booktitle")
			("year")
			("doi" nil nil 1)
			("url" nil nil 1)
			("publisher" nil nil 1))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("volume" "Volume of the book in the series")
			("number" "Number of the book in a small series (overwritten by volume)")
			("series" "Series in which the book appeared")
			("edition" "Edition of the book as a capitalized English word")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("Proceedings" "Conference Proceedings"
		 ;; required
		 (("booktitle")
			("year")
			("doi" nil nil 1)
			("url" nil nil 1)
			("publisher" nil nil 1))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("editor")
			("volume" "Volume of the conference proceedings in the series")
			("number" "Number of the conference proceedings in a small series (overwritten by volume)")
			("series" "Series in which the conference proceedings appeared")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("InProceedings" "Paper in conference proceedings"
		 ;; required
		 (("author")
			("title" "Title of the paper")
			("pages" "Pages in the conference proceedings")
			)
		 ;; required without crossref
		 (("booktitle" "Name of the paper")
			("year")
			("doi" nil nil 1)
			("url" nil nil 1)
			("publisher" nil nil 1))
		 ;; optional
		 (("volume" "Volume of the conference proceedings in the series")
			("number" "Number of the conference proceedings in a small series (overwritten by volume)")
			("series" "Series in which the conference proceedings appeared")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("InCollection" "Contribution in a collection"
		 ;; required
		 (("author")
			("title" "Title of the contribution")
			("pages" "Pages in the collection"))
		 ;; required without crossref
		 (("booktitle" "Title of the collection")
			("editor")
			("doi" nil nil 1)
			("url" nil nil 1)
			("publisher" nil nil 1)
			("year"))
		 ;; optional
		 (("volume" "Volume of the collection in the series")
			("number" "Number of the collection in a small series (overwritten by volume)")
			("series" "Series in which the collection appeared")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))
		
		("Thesis" "Thesis"
		 ;; required
		 (("author")
			("title" "Title of the thesis")
			("type" "Type of the thesis")
			("school" "School where the thesis was written" nil 1)
			("institution" "Institution where the thesis was written" nil 1)
			("year"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("url")
			("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("PhDThesis" "PhD Thesis"
		 ;; required
		 (("author")
			("title" "Title of the thesis")
			("type" "Type of the thesis")
			("school" "School where the thesis was written" nil 1)
			("institution" "Institution where the thesis was written" nil 1)
			("year"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("url")
			("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("Report" "Technical Report"
		 ;; required
		 (("author")
			("title" "Title of the technical report")
			("year")
			("type")
			("institution"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("url")
			("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))
		
		("TechReport" "Technical Report"
		 ;; required
		 (("author")
			("title" "Title of the technical report")
			("year")
			("type")
			("institution"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("url")
			("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		("Manual" "Manual"
		 ;; required
		 (("title" "Title of the manual")
			("author")
			("year")
			("url")
			("urldate"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))

		
		("Online" "Online resource"
		 ;; required
		 (("title" "Title of the online resource")
			("author")
			("year")
			("url")
			("urldate"))
		 ;; required without crossref
		 nil
		 ;; optional
		 (("doi")
			("timestamp" nil ,(format-time-string "%Y-%m-%d" (current-time)))
			("note")))
		
	  )
	"Entry types following the formatting rules of MUSS (https://github.com/timmli/biblatex-muss)."
	)

Activate MUSS dialect:

(defvar bibtex-muss-field-alist bibtex-biblatex-field-alist)
(add-to-list 'bibtex-muss-field-alist '("timestamp" "Date of creation or modification of the entry."))

(add-to-list 'bibtex-dialect-list 'muss)
(setq bibtex-dialect 'muss)

helm-bibtex

https://github.com/tmalsburg/helm-bibtex

Search through your BibTeX bibliography with helm.

(use-package helm-bibtex
	:ensure t
	;; :after (org-citeproc org-ref)   ; do not uncomment this
	:pin MELPA
	:config
	;; Make sure that the most recent version of `bibtex-completion' is installed. 
	(use-package bibtex-completion
		:ensure t
		:pin MELPA)
	(setq bibtex-completion-bibliography (list user-bibliography-file))
	(setq bibtex-completion-additional-search-fields '(bibtexkey))
	;; The standard function with modified default action  
	;; :bind (:map LaTeX-mode-map ("C-l C-r" . helm-bibtex-with-local-bibliography))
	
	;; See https://github.com/tmalsburg/helm-bibtex#reverse-order-of-entries
	(advice-add 'bibtex-completion-candidates :filter-return 'reverse)

	(setq bibtex-completion-display-formats 
				'((t . "${author:36} ${title:*} ${year:4} ${=has-pdf=:1}${=has-note=:1} ${=type=:7}")))
	<<helm-bibtex-csl>>
	)

One can influence the order of actions in helm like this:

;; (helm-delete-action-from-source "Open PDF, URL or DOI" helm-source-bibtex)
;; (helm-delete-action-from-source "Open PDF file (if present)" helm-source-bibtex)
;; (helm-delete-action-from-source "Open URL or DOI in browser" helm-source-bibtex)
;; (helm-delete-action-from-source "Show entry" helm-source-bibtex)

;; (helm-add-action-to-source "Show entry"
;; 													 'bibtex-completion-show-entry
;; 													 helm-source-bibtex 5)
;; (helm-add-action-to-source "Open PDF, URL or DOI"
;; 													 'bibtex-completion-open-any
;; 													 helm-source-bibtex 6)
;; (helm-add-action-to-source "Open PDF file (if present)"
;; 													 'bibtex-completion-open-pdf
;; 													 helm-source-bibtex 7)
;; (helm-add-action-to-source "Open URL or DOI in browser"
;; 													 'bibtex-completion-open-url-or-doi
;; 													 helm-source-bibtex 8)

;; ;; Temporarily move "Show entry" to the first position of the action list when in bibtex-mode
;; (defun tl/promote_show_entry (&optional arg local-bib)
;; 	(helm-delete-action-from-source "Show entry" helm-source-bibtex)
;; 	(helm-add-action-to-source "Show entry"
;; 														 'bibtex-completion-show-entry
;; 														 helm-source-bibtex 0))
;; (defun tl/demote_show_entry (&optional arg local-bib)
;; 	(helm-delete-action-from-source "Show entry" helm-source-bibtex)
;; 	(helm-add-action-to-source "Show entry"
;; 														 'bibtex-completion-show-entry
;; 														 helm-source-bibtex 5))
;; (defun tl/helm-bibtex-in-bibtex-mode ()
;; 	(interactive)
;; 	(tl/promote_show_entry)
;; 	(helm-bibtex)
;; 	(tl/demote_show_entry))
;; (define-key bibtex-mode-map (kbd "C-c h") 'tl/helm-bibtex-in-bibtex-mode)
;; (define-key bibtex-mode-map (kbd "C-c C-h") 'tl/helm-bibtex-in-bibtex-mode)

Support for CSL, see tmalsburg/helm-bibtex#235 (comment).

(defvar helm-bibtex-csl-style "~/.emacs.d/csl/minimal-unified-style-sheet-for-computational-linguistics.csl")

(defvar helm-bibtex-csl-locale-dir "~/.emacs.d/csl")

(defun helm-bibtex-insert-citeproc-reference (_candidate)
  (let* ((locale-getter (citeproc-locale-getter-from-dir helm-bibtex-csl-locale-dir))
         (item-getter (citeproc-itemgetter-from-bibtex helm-bibtex-bibliography))
         (proc (citeproc-create helm-bibtex-csl-style item-getter locale-getter))
         (cites (mapcar (lambda (x) (citeproc-citation-create :cites `(((id . ,x)))))
                        (helm-marked-candidates))))
    (citeproc-append-citations cites proc)
    (insert (car (citeproc-render-bib proc 'plain)))))

(helm-add-action-to-source "Insert citeproc-formatted reference" 'helm-bibtex-insert-citeproc-reference helm-source-bibtex)

Beamer

  • Note taken on [2017-11-13 Mo 10:07]
    Add \begin{frame} to outline (and imenu):
    (add-to-list 'TeX-outline-extra '("\\\\begin{frame}\\(\\[.*\\]\\)?" 4))
        

    FIXME: Does not show the title.

Add \frametitle to outline (and imenu):

(add-to-list 'TeX-outline-extra '("\\\\frametitle\\b" 4))

Misc

Make square brackets indent correctly (testing):

;; (modify-syntax-entry ?\[ "(]" LaTeX-mode-syntax-table)
;; (modify-syntax-entry ?\] ")[" LaTeX-mode-syntax-table)

;; Proposed by https://emacs.stackexchange.com/a/35507/12336
;; Overwrites TeX-brace-count-line from tex.el
(defun TeX-brace-count-line ()
  "Count number of open/closed braces."
  (save-excursion
    (let ((count 0) (limit (line-end-position)) char)
      (while (progn
               (skip-chars-forward "^{}[]\\\\" limit)
               (when (and (< (point) limit) (not (TeX-in-comment)))
                 (setq char (char-after))
                 (forward-char)
                 (cond ((eq char ?\{)
                        (setq count (+ count TeX-brace-indent-level)))
                       ((eq char ?\})
                        (setq count (- count TeX-brace-indent-level)))
                       ((eq char ?\[)
                        (setq count (+ count TeX-brace-indent-level)))
                       ((eq char ?\])
                        (setq count (- count TeX-brace-indent-level)))
                       ((eq char ?\\)
                        (when (< (point) limit)
                          (forward-char)
                          t))))))
      count)))

Delete macro at point (http://emacs.stackexchange.com/a/7997/12336):

(defun TeX-delete-macro ()
  "Remove current macro and return `t'.  If no macro at point,
return `nil'."
  (interactive)
  (when (TeX-current-macro)
    (let ((bounds (TeX-find-macro-boundaries))
          (brace  (save-excursion
                    (goto-char (1- (TeX-find-macro-end)))
                    (TeX-find-opening-brace))))
      (delete-region (1- (cdr bounds)) (cdr bounds))
      (delete-region (car bounds) (1+ brace)))
    t))

Delete environment at point (https://www.reddit.com/r/emacs/comments/5f99nv/help_with_auctex_how_to_delete_an_environment/dailbtu/):

(defun TeX-delete-environment ()
  (interactive)
  (when (LaTeX-current-environment)
    (save-excursion
      (let* ((begin-start (save-excursion
                            (LaTeX-find-matching-begin)
                            (point)))
             (begin-end (save-excursion
                          (goto-char begin-start)
                          (search-forward-regexp "begin{.*?}")))
             (end-end (save-excursion
                        (LaTeX-find-matching-end)
                        (point)))
             (end-start (save-excursion
                          (goto-char end-end)
                          (1- (search-backward-regexp "\\end")))))
        ;; delete end first since if we delete begin first it shifts the
        ;; location of end
        (delete-region end-start end-end)
        (delete-region begin-start begin-end)))))

<tab> should always insert a tab (instead of indentation):

;; (add-hook 'LaTeX-mode-hook #'(lambda () (setq-local tab-always-indent nil)))

Usefull extras, e.g., org-mode-like folding:

(use-package latex-extra                
  :ensure t
  :defer t
  :config (add-hook 'LaTeX-mode-hook #'latex-extra-mode))

Autocompletion

See *company.

Markdown

markdown-mode

https://jblevins.org/projects/markdown-mode/

Major mode for editing Markdown-formatted text.

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown")
	:config	(setq markdown-enable-math t))

markdown-toc

https://github.com/ardumont/markdown-toc

Insert TOC in a markdown file.

(use-package markdown-toc
	:ensure t)

PlantUML

Depends on PlantUML and Gaphviz.

(use-package plantuml-mode
  :ensure t
  :mode ("\\.plu\\'" "\\.puml\\'") 
  :init
	(setq org-plantuml-exec-mode 'plantuml
				org-plantuml-jar-path "/usr/share/plantuml/plantuml.jar") 
	(when (eq system-type 'windows-nt)
		(setq org-plantuml-jar-path
					(expand-file-name "C:/ProgramData/chocolatey/lib/plantuml/tools/plantuml.jar"))
		(setq plantuml-jar-path
					(expand-file-name "C:/ProgramData/chocolatey/lib/plantuml/tools/plantuml.jar"))))

Python

elpy

https://github.com/jorgenschaefer/elpy

Powerful environment for Python coding.

Prerequisites as for Python:

sudo pip install jedi flake8 importmagic autopep8 # Elpy's recommendation
sudo pip install pylint virtualenv epc # Zamansky's recommendation
(use-package elpy
	:ensure t
	:defer 2
	:init
	(elpy-enable)
	:config
	;; Use Flycheck instead of Flymake
	(when (load "flycheck" t t)
		(setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
		(add-hook 'elpy-mode-hook 'flycheck-mode))
	;;;; Maybe outdated
	;; (when (require 'flycheck nil t)
	;; 	(remove-hook 'elpy-modules 'elpy-module-flymake)
	;; 	(remove-hook 'elpy-modules 'elpy-module-yasnippet)
	;; 	(remove-hook 'elpy-mode-hook 'elpy-module-highlight-indentation)
	;; 	(add-hook 'elpy-mode-hook 'flycheck-mode))
	(define-key python-mode-map (kbd "C-h f") 'python-eldoc-at-point)
	;; highlight-indentation is ugly
	(add-hook 'elpy-mode-hook #'(lambda () (highlight-indentation-mode -1)))
	;; jedi is great
	(setq elpy-rpc-backend "jedi")
	;; tell elpy to use python3 (or install python-is-python3 under GNU/Linux)
	(setq elpy-rpc-python-command "python3")
	)

company-jedi

https://github.com/emacsorphanage/company-jedi

company-mode completion back-end for Python JEDI.

Prerequisites as for Python:

sudo pip install jedi
;; Use Company for auto-completion interface.
(defun my/python-mode-hook ()
  (add-to-list 'company-backends 'company-jedi))

(use-package company-jedi
  :ensure t
  :init
  (add-hook 'python-mode-hook 'my/python-mode-hook))

ein (Jupyter noteboook)

Procedure to open a notebook:

  1. externally start Jupyter server
  2. ein:notebooklist-login and insert the token of the Jupyter session (see terminal)
  3. ein:notebooklist-open and select the notebook
(use-package ein
  :ensure t
	:config
	(setq ein:completion-backend 'ein:use-ac-jedi-backend))

R

Shell scripts

The associated major modes is sh-mode, for which shell-script-mode is an alias.

Make script files (e.g. Bash scripts) with the hash-bang notation exectuable when saving them:

(add-hook 'after-save-hook
  'executable-make-buffer-file-executable-if-script-p)

Tree-Sitter

Fast incremental parsing of source code.

More general information:

Installation & configuration in Emacs:

List of available grammars, which can be installed with treesit-install-language-grammar:

(setq treesit-language-source-alist
   '((bash "https://github.com/tree-sitter/tree-sitter-bash")
     (cmake "https://github.com/uyha/tree-sitter-cmake")
     (css "https://github.com/tree-sitter/tree-sitter-css")
     (elisp "https://github.com/Wilfred/tree-sitter-elisp")
     (go "https://github.com/tree-sitter/tree-sitter-go")
     (html "https://github.com/tree-sitter/tree-sitter-html")
     (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
     (json "https://github.com/tree-sitter/tree-sitter-json")
     (make "https://github.com/alemuller/tree-sitter-make")
     (markdown "https://github.com/ikatyang/tree-sitter-markdown")
     (python "https://github.com/tree-sitter/tree-sitter-python")
     (toml "https://github.com/tree-sitter/tree-sitter-toml")
     (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
     (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
     (yaml "https://github.com/ikatyang/tree-sitter-yaml")))

By convention, major modes that use Tree-Sitter have the infix -ts- added to their name. Thankfully, Emacs 29 provides major-mode-remap-alist to replace old major modes with new “major-ts-modes” under the hood:

(setq major-mode-remap-alist
 '((yaml-mode . yaml-ts-mode)
   (bash-mode . bash-ts-mode)
   (js2-mode . js-ts-mode)
   (typescript-mode . typescript-ts-mode)
   (json-mode . json-ts-mode)
   (css-mode . css-ts-mode)
   (python-mode . python-ts-mode)))

However, as described in https://www.masteringemacs.org/article/how-to-get-started-tree-sitter, this still has some rough edges and seems to need more time to integrate properly into Emacs.

Web development

web-mode

https://web-mode.org/ https://github.com/fxbois/web-mode

(use-package web-mode										; for improved html support
	:ensure t
	:mode
	("\\.phtml\\'" . web-mode)
	("\\.tpl\\'" . web-mode)
	("\\.php\\'" . web-mode)
	("\\.[agj]sp\\'" . web-mode)
	("\\.as[cp]x\\'" . web-mode)
	("\\.erb\\'" . web-mode)
	("\\.mustache\\'" . web-mode)
	("\\.djhtml\\'" . web-mode)
	("\\.html?\\'" . web-mode)
	("\\.xml\\'" . web-mode)
	("\\.css\\'" . web-mode)
	("\\.svg\\'" . web-mode)
	:config
	;; highlight enclosing tags of the element under cursor
  (setq web-mode-enable-current-element-highlight t)
  ;; colorize CSS
	(setq web-mode-enable-css-colorization t)
)

impatient-mode

https://github.com/skeeto/impatient-mode

See the effect of your HTML as you type it.

(use-package impatient-mode
	:ensure t)

Usage: when in a HTML buffer,

  • call httpd-start & impatient-mode
  • open your browser at http://localhost:8080/imp/

XMG

(require 'xmg-mode) 

File management

Deft

https://jblevins.org/projects/deft/

Quickly browsing, filtering, and editing directories of plain text notes.

(use-package deft												; for displaying list of note files
	:ensure t
  :bind
	("<f9> o" . deft)
	("<f9> d" . deft)
  :commands (deft)
  :config (setq deft-directory my-deft-dir
                deft-extensions '("md" "org")
								deft-recursive t
								deft-use-filename-as-title t
								deft-use-filter-string-for-filename t))

dired

Dired is a powerful file browser in Emacs. Needless to say, with some tweaking, it has become my favourite file browser by far.

The following prevents Emacs from throwing Symbol's value as variable is void: dired-mode-map:

(require 'dired)

General settings

Tips: http://ergoemacs.org/emacs/emacs_dired_tips.html

Open new buffer when opening a file/directory, if necessary:

;; (define-key dired-mode-map (kbd "<return>") 'dired-find-alternate-file) ; was dired-advertised-find-file

;; (define-key dired-mode-map (kbd "<backspace>") (lambda () (interactive) (find-alternate-file ".."))) ; find-alternate-file forgets the cursor position of the left dired buffer
;; (define-key dired-mode-map (kbd "<left>") (lambda () (interactive) (find-alternate-file "..")))

(define-key dired-mode-map (kbd "<left>") (lambda () (interactive) (diredp-up-directory)))
(define-key dired-mode-map (kbd "<backspace>") (lambda () (interactive) (diredp-up-directory)))

View file and return to dired with q:

(define-key dired-mode-map (kbd "<tab>") 'dired-view-file)
(define-key dired-mode-map (kbd "<right>") 'dired-view-file)
(add-hook 'view-mode-hook
  (lambda ()
    (define-key view-mode-map (kbd "<left>") 'View-quit)
		(define-key view-mode-map (kbd "<right>") 'View-exit-and-edit)
    ))

Copy/move into other dired buffer:

(setq dired-dwim-target t)

Show details of files and directories:

(add-hook 'dired-mode-hook
          (lambda () (dired-hide-details-mode -1)))

Don’t break lines:

(add-hook 'dired-initial-position-hook #'(lambda () (setq truncate-lines t)))
;; (add-hook 'dired-mode-hook (lambda () (setq truncate-lines t)))

Order directories first (only windows?):

(setq ls-lisp-dirs-first t)

Ignore case when sorting:

(setq ls-lisp-ignore-case t)

Other settings: http://oremacs.com/2015/01/13/dired-options/

(setq dired-listing-switches "-laGh1v --group-directories-first") ; --group-directories-first is only supported by recent versions of ls.

Specify ls program: https://www.gnu.org/software/emacs/manual/html_node/efaq-w32/Dired-ls.html

(setq ls-lisp-use-insert-directory-program t)	; use external ls
;; (setq insert-directory-program "c:/tools/cygwin/bin/ls") ; path to ls program

Chose Windows drives:

(when (eq system-type 'windows-nt)
	(define-key dired-mode-map (kbd "\\") 'diredp-w32-drives))

Move to trash can (recycle bin under windows) when deleting:

(setq delete-by-moving-to-trash t)

<mouse-1> opens file/directory in the same window:

(define-key dired-mode-map (kbd "<mouse-2>") 'dired-mouse-find-file)

Mark/Unmark

Use shift-up and shift-down in the usual way. See http://emacs.stackexchange.com/a/30890/12336

(defun my-dired-toggle (arg)
  (save-restriction
    (narrow-to-region (point-at-bol) (point-at-eol))
    (dired-toggle-marks))
	(dired-previous-line arg))

(defun my-dired-mark-up ()
  (interactive)
  (my-dired-toggle 1))

(defun my-dired-mark-down ()
  (interactive)
  (my-dired-toggle -1))

(define-key dired-mode-map (kbd "<S-up>") 'my-dired-mark-up)
(define-key dired-mode-map (kbd "<S-down>") 'my-dired-mark-down)

Open with external program

dired-open-with

https://github.com/FrostyX/dired-open-with

Select external program to open a file.

(use-package dired-open-with
  :load-path "lisp/"
  ;; :ensure t
)
(define-key dired-mode-map (kbd "C-<return>") 'dired-open-with)
xah-open-in-external-app
(defun xah-open-in-external-app (&optional @fname)
  "Open the current file or dired marked files in external app.
The app is chosen from your OS's preference.

When called in emacs lisp, if @fname is given, open that.

URL `http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html'
Version 2019-11-04"
  (interactive)
  (let* (
         ($file-list
          (if @fname
              (progn (list @fname))
            (if (string-equal major-mode "dired-mode")
                (dired-get-marked-files)
              (list (buffer-file-name)))))
         ($do-it-p (if (<= (length $file-list) 5)
                       t
                     (y-or-n-p "Open more than 5 files? "))))
    (when $do-it-p
      (cond
       ((string-equal system-type "windows-nt")
        (mapc
         (lambda ($fpath)
           (w32-shell-execute "open" $fpath)) $file-list))
       ((string-equal system-type "darwin")
        (mapc
         (lambda ($fpath)
           (shell-command
            (concat "open " (shell-quote-argument $fpath))))  $file-list))
       ((string-equal system-type "gnu/linux")
        (mapc
         (lambda ($fpath) (let ((process-connection-type nil))
                            (start-process "" nil "xdg-open" $fpath))) $file-list))))))
;; (define-key dired-mode-map (kbd "C-<return>") 'xah-open-in-external-app)

Duplicate file

Duplicate file at point while changing the file name.

(defun tl/dired-duplicate-this-file ()
  "Duplicate file on this line.
Taken from https://emacs.stackexchange.com/a/60663/12336."
  (interactive)
  (let* ((this  (dired-get-filename t))
         (ctr   1)
         (new   (format "%s[%d]" this ctr)))
    (while (file-exists-p new)
      (setq ctr  (1+ ctr)
            new  (format "%s[%d]" this ctr)))
    (dired-copy-file this new nil))
  (revert-buffer))

Special keys

Copy path

Sometimes its useful to copy the path of a file or directory under cursor:

(defun tl/dired-copy-path-at-point ()
	(interactive)
	(dired-copy-filename-as-kill 0)
)

(define-key dired-mode-map (kbd "W") 'tl/dired-copy-path-at-point)
Delete

Delete with <delete> key.

(define-key dired-mode-map (kbd "<delete>") 'dired-do-delete)

dired-imenu

(use-package dired-imenu
	:ensure t
	:init
	(require 'dired-imenu)
)

dired-k

https://github.com/emacsorphanage/dired-k

Enrich dired buffer with git information.

(use-package dired-k
	:ensure t
	:bind (:map dired-mode-map ("K" . dired-k))
	:config
  (progn
		;; FIXME: Tramp prompts for the password in every new dired buffer!
		;; (add-hook 'dired-initial-position-hook 'dired-k)
		;; (add-hook 'dired-after-readin-hook #'dired-k-no-revert)
))

git-ps1-mode

https://github.com/10sr/git-ps1-mode-el

Show git branch in modeline.

(use-package git-ps1-mode
	:ensure t
	:pin MELPA
	:config
	(git-ps1-mode t))

dired-quick-sort

https://gitlab.com/xuhdev/dired-quick-sort

Depends on ls, the version of which should support --group-directories-first. Activated in dired with <S-s>.

(use-package dired-quick-sort
	:ensure t
	:init
	(require 'dired-quick-sort)
	(dired-quick-sort-setup)
	)

dired+

https://www.emacswiki.org/emacs/DiredPlus

Downloaded from here: https://github.com/emacsmirror/emacswiki.org/blob/master/dired%2b.el

(use-package dired+
  :config
  (require 'dired+)
  (setq diredp-hide-details-initially-flag nil)
  (set-face-foreground 'diredp-file-name nil)
  ;; Keep dired buffers updated when the file system changes.
  (setq global-auto-revert-non-file-buffers t)
  (setq auto-revert-verbose nil)
  (define-key dired-mode-map (kbd "<mouse-2>") 'dired-mouse-find-file) ; Just a reminder for dired+ ;-)
)

dired-hacks

Several helper packages for dired. See documentation here: https://github.com/Fuco1/dired-hacks

dired-narrow

Filter dired list on the fly:

(use-package dired-narrow
  :ensure t
  :bind (:map dired-mode-map
							("/" . dired-narrow)
							:map dired-narrow-map
              ("<tab>" . dired-narrow-enter-directory)
              ("<right>" . dired-view-file)
							("<return>" . exit-minibuffer))        
	:config
	(setq dired-narrow-exit-action 'dired-narrow-find-file)
)
dired-rainbow

More customizable highlightning in dired listings. Settings inspired by

(defconst my-dired-media-files-extensions '("mp3" "mp4" "MP3" "MP4" "avi" "mpg" "flv" "ogg" "wmv" "mkv" "mov" "wma")
  "Media file extensions that should launch in VLC.
Also used for highlighting.")
(use-package dired-rainbow
	:ensure t 
  :config
  (progn
    (dired-rainbow-define html "#4e9a06" ("htm" "html" "xhtml"))
    (dired-rainbow-define xml "#b4fa70" ("xml" "xsd" "xsl" "xslt" "wsdl"))

    (dired-rainbow-define document "#fce94f" ("doc" "docx" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub"))
    (dired-rainbow-define excel "#3465a4" ("xlsx"))
    (dired-rainbow-define media "#ce5c00" my-dired-media-files-extensions)
    (dired-rainbow-define image "#ff4b4b" ("jpg" "png" "jpeg" "gif"))

    (dired-rainbow-define log "#c17d11" ("log"))
    (dired-rainbow-define sourcefile "#fcaf3e" ("py" "c" "cc" "h" "java" "pl" "rb" "R" "php"))

    (dired-rainbow-define executable "#8cc4ff" ("exe" "msi"))
    (dired-rainbow-define compressed "#ad7fa8" ("zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar"))
    (dired-rainbow-define packaged "#e6a8df" ("deb" "rpm"))
    (dired-rainbow-define encrypted "LightBlue" ("gpg" "pgp"))

    (dired-rainbow-define-chmod executable-unix "Green" "-.*x.*")
    ))
dired-ranger

https://github.com/Fuco1/dired-hacks#dired-ranger

Adds conventional copy & paste behaviour to dired.

See blog post at http://pragmaticemacs.com/emacs/copy-and-paste-files-with-dired-ranger/.

(use-package dired-ranger
	:ensure t
  :config
  (setq dired-ranger-copy-ring-size 1)
	(define-key dired-mode-map (kbd "C-w")
		(lambda ()
			(interactive)
			(dired-ranger-copy nil)	; t adds item to dired-ranger-copy-ring
			(define-key dired-mode-map (kbd "C-y") 'dired-ranger-move)))
	(define-key dired-mode-map (kbd "M-w")
		(lambda ()
			(interactive)
			(dired-ranger-copy nil)
			(define-key dired-mode-map (kbd "C-y") 'dired-ranger-paste)))
)
dired-collapse

Show whole path to contained file or directory if there is only one.

See https://github.com/Fuco1/dired-hacks#dired-collapse.

(use-package dired-collapse
	:ensure t
	:init
	(add-hook 'dired-mode-hook #'dired-collapse-mode))

dired-efap

Rename file name at point.

(use-package dired-efap
	:ensure t
	:config
	(setq dired-efap-initial-filename-selection nil)
	(define-key dired-mode-map (kbd "r") 'dired-efap)
	)

ediff

Quick ediff on marked files. Copied from https://oremacs.com/2017/03/18/dired-ediff/:

(defun ora-ediff-files ()
  (interactive)
  (let ((files (dired-get-marked-files))
        (wnd (current-window-configuration)))
    (if (<= (length files) 2)
        (let ((file1 (car files))
              (file2 (if (cdr files)
                         (cadr files)
                       (read-file-name
                        "file: "
                        (dired-dwim-target-directory)))))
          (if (file-newer-than-file-p file1 file2)
              (ediff-files file2 file1)
            (ediff-files file1 file2))
          (add-hook 'ediff-after-quit-hook-internal
                    (lambda ()
                      (setq ediff-after-quit-hook-internal nil)
                      (set-window-configuration wnd))))
      (error "no more than 2 files should be marked"))))

(define-key dired-mode-map "e" 'ora-ediff-files)

diffpdf

https://github.com/ShuguangSun/diffpdf.el

Use diffpdf in dired.

(use-package diffpdf
	:ensure t)

vdiff

https://github.com/justbur/emacs-vdiff

Vimdiff for Emacs.

(use-package vdiff
	:ensure t
	:pin MELPA
	:config
	(setq vdiff-auto-refine t) ; If non-nil, automatically refine all hunks.
	)

imenu

Integrate imenu into dired. Copied from https://fuco1.github.io/2017-05-01-Support-for-imenu-in-dired.html

(defun my-dired-imenu-prev-index-position (&optional arg)
  "Go to the header line of previous directory."
  (interactive "p")
  (unless (= (line-number-at-pos) 1)
    (call-interactively 'dired-prev-subdir)
    t))

(defun my-dired-extract-index-name ()
  "Extract name of the current item for imenu."
  (save-excursion
    (back-to-indentation)
    (buffer-substring-no-properties
     (point)
     (1- (re-search-forward ":$")))))

(defun my-dired-imenu-create-index ()
  "Create `imenu' index for dired."
  (let* ((alist (imenu-default-create-index-function))
         (uniquified (f-uniquify-alist (-map 'car alist))))
    (--remove
     (= 0 (length (car it)))
     (--map (cons (cdr (assoc (car it) uniquified)) (cdr it))
            alist))))

(defun my-dired-imenu-init ()
  "Initialize `imenu' variables in current buffer."
  (setq-local imenu-prev-index-position-function
              'my-dired-imenu-prev-index-position)
  (setq-local imenu-extract-index-name-function
              'my-dired-extract-index-name)
  (setq-local imenu-create-index-function
              'my-dired-imenu-create-index))

(add-hook 'dired-mode-hook 'my-dired-imenu-init)

History

  • [ ] I would like to be able to browse the dired history. helm-dired-history could be useful for this, but I don’t know how to use it to this end.

Size info

Calculates the size of marked directories using du. Copied from https://oremacs.com/2015/01/12/dired-file-size/ and slightly adapted.

(defun dired-get-size ()
"Print size of marked files or directories using shell command `du'."
  (interactive)
  (let ((files (dired-get-marked-files)))
    (with-temp-buffer
      (apply 'call-process "du" nil t nil "-sch" files)
      (message
       "Size of all marked files: %s"
       (progn
         (re-search-backward "\\(^[ 0-9.,]+\\([A-Za-z]+\\)?\\).*total$")
         (match-string 1))))))

(define-key dired-mode-map (kbd "z") 'dired-get-size)

disk-usage

https://gitlab.com/ambrevar/emacs-disk-usage

A file system analyzer similar to ncdu. However, I prefer using the latter.

(use-package disk-usage
	:load-path "lisp/disk-usage/")

ztree

https://codeberg.org/fourier/ztree

Show folder structure as foldable tree, also allowing for diffs on folders with ztree-diff.

(use-package ztree
	:ensure t)

Getting things done

Pomodoro technique

pomidor

https://github.com/TatriX/pomidor/

Pomidor brings the Pomodoro technique to Emacs.

(use-package pomidor
	:ensure t
	:init
	(setq pomidor-sound-tick nil
				pomidor-sound-tack nil
				pomidor-sound-overwork t)
	)

Typing tutor

gtypist

GNU Typist: An interactive typing tutor https://www.gnu.org/software/gtypist/doc/gtypist.html

There seems to be a mode for Emacs: https://www.gnu.org/software/gtypist/doc/gtypist.html#Emacs-mode

Messaging

Email

Helper functions

Functions to extract email addresses from strings and buffers:

(defun tl/extract-email-addresses-in-buffer (&optional separator)
	"Extract email addresses from current buffer and return them as string mit SEPARATOR."
	(or (stringp separator)
			(setq separator ", "))
	(save-excursion
		(goto-char (point-min))
		(let ((email-list '()))
			(while (search-forward "@" nil t)
				(let ((email (thing-at-point 'email)))
					(when email
						(setq email-list (cons email email-list)))))
			(setq email-list (sort email-list 'string<))
			(mapconcat 'identity email-list separator))))

(defun tl/extract-email-addresses-from-string (string &optional separator)
	"Extract email addresses from STRING and return them as string mit SEPARATOR."
	(with-temp-buffer
		(insert string)
		(goto-char (point-min))
		(tl/extract-email-addresses-in-buffer separator)))

(defun tl/insert-string-to-new-buffer (string)
	"Insert STRING into new buffer and go to this buffer."
	(let ((new-buffer (generate-new-buffer "*temp*")))
		(switch-to-buffer new-buffer)
		(insert string)
		(goto-char (point-min))))

;; Usage:
;; (tl/insert-string-to-new-buffer (tl/extract-email-addresses-in-buffer "\n"))

Interoperability with Thunderbird

The Exteditor plugin can be used in Thunderbird in order to open and edit an email in Emacs.

Open eml-files with org-mode turned on:

(add-to-list 'auto-mode-alist '("\\.eml\\'" . org-mode))

mu4e

https://www.djcbsoftware.nl/code/mu/mu4e/index.html https://github.com/djcb/mu

Mu4e is an email client for Emacs that uses mu, which constructs a Xapian database for emails in the Maildir format for faster search.

Note that it is recommended to use the version of mu4e that is shipped together with the installed mu. Currently supported version: 1.12.x

Mu4e and Mu do not synchronize local and remote mailboxes. Below I’m assuming that mbsync is used for this purpose. (A popular alternative might be offlineimap.) The following tutorial gives an idea how to set up mbsync:

Good examples of elaborate mu4e configurations:

Email workflow with org-capture:

Installation & general settings
(add-to-list 'load-path (expand-file-name "lisp/mu4e" user-emacs-directory))
(require 'mu4e)

;; Home of the Xapian index
;; Note that mu does not like to expand ~, as explained here: 
;; https://github.com/djcb/mu/blob/release/1.10/NEWS.org#110-releases-on-march-26-2023
(setq mu4e-mu-home "/home/USER/.cache/mu")

;; Refresh rate of emails
(setq mu4e-update-interval (* 5 60))

;; Close mu4e without asking.
(setq mu4e-confirm-quit nil)

;; Set a sane ISO 8601 date format.
(setq mu4e-headers-date-format "%+4Y-%m-%d")

;; This allows for using 'helm' to select mailboxes
(setq mu4e-completing-read-function 'completing-read)

;; Mute index messages 
(setq mu4e-hide-index-messages t)

;; Keybindings
(define-key mu4e-headers-mode-map (kbd "M") #'mu4e)
(define-key mu4e-view-mode-map (kbd "M") #'mu4e)
Accounts
;; Default account on startup
(setq
 mu4e-mu-home "~/.cache/mu" 			; as of v1.4
 ;; mu4e-maildir  "~/Maildir" ; deprecated in v1.4
 mu4e-sent-folder "/Sent"
 mu4e-drafts-folder "/Drafts"
 mu4e-trash-folder "/Trash")

(setq mu4e-get-mail-command "mbsync -c ~/SOME_PATH/.mbsyncrc work"
      mu4e-attachment-dir "~/Downloads" ; where to store attachments
)

;; Update mails every 5 minutes and in the background
(setq mu4e-update-interval (* 5 60))
(setq mu4e-index-update-in-background t)

;; Always sign outgoing emails
;; (setq mu4e-compose-crypto-reply-plain-policy 'sign)

Note that each mail account must be indexed by mu before it can be accessed with mu4e:

mu init --maildir=~/SOME_PATH/mbsyncmail/
mu index
Contexts

Contexts are bundles of settings that can, for example, represent different mail accounts. Contexts are defined in mu4e-context, see the manual for details: https://www.djcbsoftware.nl/code/mu/mu4e/What-are-contexts.html

The context can be chosen automatically or manually. This is controlled by the variables mu4e-context-policy and compose-context-policy. See the manual for more details: https://www.djcbsoftware.nl/code/mu/mu4e/Context-policies.html

(setq mu4e-context-policy 'ask
			mu4e-compose-context-policy 'ask)

Here is an example (taken from https://www.djcbsoftware.nl/code/mu/mu4e/Contexts-example.html) for how contexts are specified:

(setq mu4e-contexts
   `( ,(make-mu4e-context
         :name "Private"
         :enter-func (lambda () (mu4e-message "Entering Private context"))
         :leave-func (lambda () (mu4e-message "Leaving Private context"))
         ;; we match based on the contact-fields of the message
         :match-func (lambda (msg)
                       (when msg
                         (mu4e-message-contact-field-matches msg
                           :to "aliced@home.example.com")))
         :vars '( ( user-mail-address	    . "aliced@home.example.com"  )
                  ( user-full-name	    . "Alice Derleth" )
                  ( message-user-organization . "Homebase" )
                  ( mu4e-compose-signature .
                    (concat
                      "Alice Derleth\n"
                      "Lauttasaari, Finland\n"))))
      ,(make-mu4e-context
         :name "Work"
         :enter-func (lambda () (mu4e-message "Switch to the Work context"))
         ;; no leave-func
         ;; we match based on the maildir of the message
         ;; this matches maildir /Arkham and its sub-directories
         :match-func (lambda (msg)
                       (when msg
                         (string-match-p "^/Arkham" (mu4e-message-field msg :maildir))))
         :vars '( ( user-mail-address	     . "aderleth@miskatonic.example.com" )
                  ( user-full-name	     . "Alice Derleth" )
                  ( message-user-organization . "Miskatonic University" )
                  ( mu4e-compose-signature  .
                    (concat
                      "Prof. Alice Derleth\n"
                      "Miskatonic University, Dept. of Occult Sciences\n"))))

      ,(make-mu4e-context
         :name "Cycling"
         :enter-func (lambda () (mu4e-message "Switch to the Cycling context"))
         ;; no leave-func
         ;; we match based on the maildir of the message; assume all
         ;; cycling-related messages go into the /cycling maildir
         :match-func (lambda (msg)
                       (when msg
                         (string= (mu4e-message-field msg :maildir) "/cycling")))
         :vars '( ( user-mail-address	     . "aderleth@example.com" )
                  ( user-full-name	     . "AliceD" )
                  ( mu4e-compose-signature  . nil)))))

 ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
 ;; guess or ask the correct context, e.g.

 ;; start with the first (default) context;
 ;; default is to ask-if-none (ask when there's no context yet, and none match)
 ;; (setq mu4e-context-policy 'pick-first)

 ;; compose with the current context is no context matches;
 ;; default is to ask
 ;; (setq mu4e-compose-context-policy nil)
View headers
;; How to show messages / headers.
;; A symbol which is either:
;;  * ‘horizontal’:    split horizontally (headers on top)
;;  * ‘vertical’:      split vertically (headers on the left).
;;  * ‘single-window’: view and headers in one window (mu4e will try not to
;; 		    touch your window layout), main view in minibuffer
;;  * anything else:   don’t split (show either headers or messages,
;; 		    not both)
(setq mu4e-split-view 'vertical)
(setq mu4e-headers-visible-lines 10)
(setq mu4e-headers-visible-columns 100)

;; Look of mail headers
;; The following is an attempt to truncate lines in mu4e-headers-mode.
(add-hook 'mu4e-headers-mode-hook
					#'(lambda ()
							(with-current-buffer "*mu4e-headers*"
								(toggle-truncate-lines t))))
(add-hook 'mu4e-headers-found-hook
					#'(lambda ()
							(with-current-buffer "*mu4e-headers*"
								(toggle-truncate-lines t))))

;; Change some text faces
(set-face-attribute 'mu4e-replied-face nil :inherit 'font-lock-function-name-face)
(set-face-attribute 'mu4e-forwarded-face nil :inherit 'mu4e-header-value-face)
(set-face-attribute 'mu4e-related-face nil :inherit 'font-lock-comment-face)

;; Columns shown 
(setq mu4e-headers-fields '(
                            (:human-date    . 12)
                            (:flags         .  6)
                            ;; (:mailing-list  .   10)
                            (:from          . 22)
														(:to            . 22)
                            (:subject       . nil)))

Visible flags are listed in mu4e-headers-visible-flags. Only a small subset of flags is needed since the other ones are redundant:

(setq mu4e-headers-visible-flags '(flagged attach encrypted list calendar))

Flag symbols can be easily changed. Let’s try something fancier, as proposed in mu4e-headers.el:

(setq mu4e-headers-draft-mark     '("D" . "💈")
			mu4e-headers-flagged-mark   '("F" . "📍")
			mu4e-headers-new-mark       '("N" . "🔥")
			mu4e-headers-passed-mark    '("P" . "")
			mu4e-headers-replied-mark   '("R" . "")
			mu4e-headers-seen-mark      '("S" . "")
			mu4e-headers-trashed-mark   '("T" . "💀")
			mu4e-headers-attach-mark    '("a" . "📎")
			mu4e-headers-encrypted-mark '("x" . "🔒")
			mu4e-headers-signed-mark    '("s" . "🔑")
			mu4e-headers-unread-mark    '("u" . "")
			mu4e-headers-list-mark      '("l" . "🔈")
			mu4e-headers-personal-mark  '("p" . "👨")
			mu4e-headers-calendar-mark  '("c" . "📅"))

Actions:

(setq mu4e-headers-actions
			'(("capture message" . mu4e-action-capture-message)
				("narrow to thread" . mu4e-action-show-thread)
				("save email as file" . tl/mu4e-action-copy-message-file)))

Keys:

;; v1.12 standardly uses C-<tab> for folding threads.
(define-key mu4e-thread-mode-map (kbd "C-<tab>") nil) 
View messages
;; Message viewer settings
;; (setq mu4e-view-use-old t)							; use Gnus; since v1.6
;; (setq mu4e-view-use-gnus t)						; deprecated in v1.6

;; show full addresses in view message (instead of just names)
;; toggle per name with M-RET
(setq mu4e-view-show-addresses t)

;; Customize header fields to show in mu4e-view. This only adds :bcc and :size.
(setq mu4e-view-fields '(:from :to :cc :bcc :subject :flags :date :maildir :mailing-list :tags :attachments :size :signature :decryption))

;; Show images inline
(setq mu4e-view-show-images t)

;; Allow fancy (Unicode) characters for marks/threads
(setq mu4e-use-fancy-chars t)

;; to view selected message in the browser, no signin, just html mail
;; (add-to-list 'mu4e-view-actions
;;   '("ViewInBrowser" . mu4e-action-view-in-browser) t)

;; Automatic line breaks when reading mail. 
;; Disabled for now, because visual-line-mode may be invoked by w, if needed.
;; (add-hook 'mu4e-view-mode-hook #'visual-line-mode)

Since v1.10, messages can be detached in mu4e-view-mode. The following functions detaches a displayed message and opens it in new frame:

(defun tl/mu4e-detach-and-view-message-in-new-frame ()
	(interactive)
	(when (mu4e--view-mode-p)
		(mu4e-view-detach)
		(make-frame)
		(view-buffer "*mu4e-headers*") 			; Interestingly does not work with with-current-buffer 
		(keyboard-escape-quit)))
(define-key mu4e-view-mode-map "z" 'tl/mu4e-detach-and-view-message-in-new-frame)

Actions:

(setq mu4e-view-actions
			'(("capture message" . mu4e-action-capture-message)
				("view in browser" . mu4e-action-view-in-browser)
				("xview in xwidget" . mu4e-action-view-in-xwidget)
				("narrow to thread" . mu4e-action-show-thread)
				("save email as file" . tl/mu4e-action-copy-message-file)))
Remove attachments

The following code is taken from djcb/mu#2247 (comment). It depends on alterMIME.

(defun bjm/mime-part-filename (num)
  "Filename of MIME part numbered num in gnus-article-mode."
  ;; Check whether the specified part exists.
  (when (> num (length gnus-article-mime-handle-alist))
    (error "No such part"))
  ;; Move point to MIME part
  (when (gnus-article-goto-part num)
    ;; Get handle for MIME part at point
    (let ((handle (get-text-property (point) 'gnus-data)))
      (when handle
        ;; Return file name of handle
        (mm-handle-filename handle)))))

(defun bjm/mu4e-delete-attachment (num)
  "Remove email attachment from mu4e using altermime."
  (let* ((path (mu4e-message-field (mu4e-message-at-point) :path))
         (filename (bjm/mime-part-filename num))
         (cmd (format "altermime --input='%s' --remove='%s' --xheader='X-Attachment-Removed: %s'" path filename filename)))
    (when (and filename
               (yes-or-no-p
                (format "Remove '%s'?" filename)))
      (shell-command cmd)
      (mu4e-message cmd))))

;; get custom header field
(defun bjm/mu4e-fetch-field (msg hdr)
  "Find the value for an arbitrary header field HDR from MSG."
  (with-temp-buffer
    (insert-file-contents (plist-get msg :path))
    (message-fetch-field hdr)))

;; display attachmment removed field
(add-to-list 'mu4e-header-info-custom
             '(:xremoved . (:name "X-Attachment-Removed"
																	:shortname "XAR"
																	:help "Name of attachment removed"
																	:function (lambda (msg) (or (bjm/mu4e-fetch-field msg "X-Attachment-Removed") "")))))

;; display this header field in message view
(add-to-list 'mu4e-view-fields :xremoved t)

(add-to-list 'mu4e-view-mime-part-actions
             '(:name "delete-attachment"
										 :handler bjm/mu4e-delete-attachment
										 :receives index))
iCalender support
  • State “TODO” from [2024-05-27 Mon 11:49]
  • [ ] Adapt the Org heading generated for the event
    • See function gnus-icalendar-event->org-entry (not yet customizable)

With the help of Gnus, iCalendar invitations can be viewed and replied to. Documentation: https://www.djcbsoftware.nl/code/mu/mu4e/iCalendar.html

(require 'mu4e-icalendar)
;; (mu4e-icalendar-setup) ; obsolete since around v1.12
(gnus-icalendar-setup) 

Furthermore, with Gnus, one can convert iCalendar invitations to Org headings using the Org capture mechanism.

(setq gnus-icalendar-org-capture-file (expand-file-name "captures.org" org-directory))
(setq gnus-icalendar-org-capture-headline "Calendar")

Since the default template for Gnus captures does not work, I’m using the following one:

(add-to-list 'org-capture-templates
						 `("#" "used by gnus-icalendar-org" entry
						   (file+headline ,gnus-icalendar-org-capture-file
															,gnus-icalendar-org-capture-headline)
						   "%i" :jump-to-captured t :prepend t)
						 )

Here is the one that Gnus would use otherwise:

("#" "used by gnus-icalendar-org" entry
   (file+olp ,gnus-icalendar-org-capture-file
             ,gnus-icalendar-org-capture-headline)
   "%i" :immediate-finish t)

Finally, activate the iCalendar-to-Org export:

(gnus-icalendar-org-setup)
Show size of each attachment
  • State “TODO” from [2024-05-02 Thu 23:12]
Display attachments

The variable gnus-mime-button-line-format controls the text of Gnus buttons for attachments.

Valid specifiers include: %t The MIME type %T MIME type, along with additional info %n The ‘name’ parameter %d The description, if any %l The length of the encoded part %p The part identifier number %e Dots if the part isn’t displayed

The following changes the default look of buttons.

(setq gnus-mime-button-line-format "%{%([%p. \"%n\" – %t]%)%}%e
")

Since Gnus removes line breaks (which I want) with skip-chars-backward when “buttonizing” attachments, the following disables skip-chars-backward locally. Not optimal, but it works for now. See the discussion here: djcb/mu#1983

(defun tl/advice-gnus-mime-buttonize-attachments-in-header (orig-fun &rest args)
  "Advice for `gnus-mime-buttonize-attachments-in-header` that locally disables `skip-chars-backward`."
	(cl-letf (((symbol-function 'skip-chars-backward)
						 (lambda (&rest args) nil)))
		(apply orig-fun args)))

(advice-add 'gnus-mime-buttonize-attachments-in-header
            :around #'tl/advice-gnus-mime-buttonize-attachments-in-header)
Compose messages
(add-hook 'mu4e-compose-mode-hook
					(defun my-mu4e-compose-settings ()
						"My settings for message composition."
						(company-mode) 							; Complete addresses with company
						(save-excursion (message-goto-bcc)
														(when (not (looking-at "[[:blank:]]*$"))        
															(kill-line)) ; Avoid double insertions
														(insert user-mail-address))
						(save-excursion (message-goto-cc))

						;; Remove the first date field (but not the one in forwarded messages).    
						;; Otherwise, the date of the composing start is used!
 						;; https://github.com/djcb/mu/issues/2096
						(message-remove-header "Date" nil t)

						;; Replace citation line place keeper.
						(save-excursion
							(beginning-of-buffer)
							(when (re-search-forward message-citation-line-format nil t)
								(replace-match (tl/message-citation-line-string) t)))

						;; General fill settings
						(turn-off-auto-fill)
						(set-fill-column 79)

						;; Activate spell checking
						(flyspell-mode)

						;; Do not autosafe drafts
						(auto-save-mode -1)
						
						;; FIXME: Does not work! 
						(when (re-search-forward "<#part sign=smime>" nil t)
							(end-of-line)
							(open-line 1)
							(forward-line))
						))

;; Only complete with mail addresses that have been used recently and with personal contact
(setq mu4e-compose-complete-only-personal t 
			mu4e-compose-complete-only-after (format-time-string
																				"%Y-%m-%d"
																				(time-subtract (current-time) (days-to-time 300))))

;; Compose email in separate frame
(setq mu4e-compose-in-new-frame t)
;; Close buffer (unfortunately not the frame) after sending the email
(setq message-kill-buffer-on-exit t)

;; Automatically include a message-signature in new messages (if it is set).
(setq mu4e-compose-signature-auto-include t)

;; Don't reply to yourself
;; Presupposes: mu init --my-address=your.email@address
(setq mu4e-compose-dont-reply-to-self t)

Edit-as-new feature: since v1.12, this feature is supported to some extend with the function mu4e-compose-supersede. Let’s bind it to [N]:

(define-key mu4e-compose-minor-mode-map (kbd "N")
  #'mu4e-compose-supersede)

Switch between “Reply” and “Wide Reply”. As of v1.12, the key [R] will start a compose buffer replying only to the sender of the message under point. To get the old behaviour back, the following code can be used:

;; Adapted code from https://github.com/djcb/mu/issues/2665#issuecomment-2004909314
(defun compose-reply-wide-or-not-please-ask ()
  "Ask whether to reply-to-all or not."
  (interactive)
  ;; (mu4e-compose-reply (yes-or-no-p "Reply to all?"))
	(let* ((from-myself (mu4e-message-sent-by-me (mu4e-message-at-point)))
				 (addressees (append (mu4e-field-at-point ':to)
														 (mu4e-field-at-point ':cc))))
		(mu4e-compose-reply ; non-nil creates a wide reply
		 (cond ((> (length addressees) 1)
						(string-equal "a"
													(char-to-string
													 (read-char "Reply to [a]ll or only to [s]ender? "))))
					 (from-myself t)
					 (t nil)
					 ))))

(define-key mu4e-compose-minor-mode-map (kbd "R")
  #'compose-reply-wide-or-not-please-ask)

Alternatively, one could bind [R] to “wide” replies:

(define-key mu4e-compose-minor-mode-map (kbd "R")
  #'mu4e-compose-wide-reply)

Configure the body of forwarded messages:

(setq message-forward-as-mime nil
			message-forward-before-signature t)
Attachments & pasting from clipboard

In general, files should be attached at the end of a message except for inline attachments. Since mml-attach-file-at-the-end does not differentiate between inline and other attachments, it should not be used:

(setq mml-attach-file-at-the-end nil)

Attachments can be included directly from dired with the help of gnus.

Taken from https://www.djcbsoftware.nl/code/mu/mu4e/Dired.html#Dired.

(require 'gnus-dired)
;; make the `gnus-dired-mail-buffers' function also work on
;; message-mode derived modes, such as mu4e-compose-mode
(defun gnus-dired-mail-buffers ()
  "Return a list of active message buffers."
  (let (buffers)
    (save-current-buffer
      (dolist (buffer (buffer-list t))
	(set-buffer buffer)
	(when (and (derived-mode-p 'message-mode)
		(null message-sent-message-via))
	  (push (buffer-name buffer) buffers))))
    (nreverse buffers)))

(setq gnus-dired-mail-mode 'mu4e-user-agent)
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)

Paste images from clipboard:

;; Taken from https://emacs.stackexchange.com/a/73068 and slightly modified 
(defun my/clip-to-PNG ()
  (interactive)
  (when (string-match-p (regexp-quote "image/png") (shell-command-to-string "xclip -selection clipboard -o -t TARGETS"))
    (let
        ((image-file (concat "/tmp/" (format-time-string "tmp_%Y%m%d_%H%M%S.png"))))
      (shell-command-to-string (concat "xclip -o -selection clipboard -t image/png > " image-file))
      image-file)))

(defun my/mu4e-attach-image-from-clipboard ()
  (interactive)
  (let ((image-file (my/clip-to-PNG)) ;; paste clipboard to temp file
        (pos (point-marker)))
    (when image-file
			;; Add image as an external attachment
      ;; (goto-char (point-max))
      ;; (mail-add-attachment image-file)

			;; Add image as an internal attachment
			(mml-attach-file image-file "image/png" nil "inline")

			)))

When inserting formatted content from the clipboard, this is first converted to appropriate Markdown markup:

(defun message-insert-formatted-text-from-clipboard ()
	"Insert formatted clipboard content with Org markup." 
	(interactive)
	(shell-command "xclip -o -t text/html | pandoc -f html -t gfm-raw_html -" '(4)))
(define-key mu4e-compose-mode-map (kbd "s-y") 'message-insert-formatted-text-from-clipboard)

Copy as formatted text to the clipboard:

(defun markdown-copy-formatted-text-to-clipboard ()
  "Export region to HTML, and copy it to the clipboard."
  (interactive)
	(if (use-region-p)
			(shell-command-on-region
			 (region-beginning)
			 (region-end)
			 "pandoc -t html -f gfm-raw_html - | xclip -verbose -selection clipboard -t text/html"
			 )))
(define-key mu4e-compose-mode-map (kbd "s-w") 'markdown-copy-formatted-text-to-clipboard)

Note that xclip will wait (and Emacs will be stuck) until the copied content is pasted!

Sanity checks

Check whether the message has an attachment if it mentions one:

(defun tl/mu4e-message-has-attachment-p ()
	"Check whether the message has an attachment."
	(save-excursion
		(message-goto-body-1)
		(search-forward-regexp "<#part .* disposition=attachment" nil t)))

(require 'subr-x)
(defun tl/mu4e-message-mentions-attachment-p ()
	"Check whether the message mentions an attachment."
	(save-excursion
		(message-goto-body-1)
		(let ((case-fold-search nil)
					(keywords '("attached"
											"attachment"
											"angehängt"
											"Anhang"
											"anbei"
											"häng.*an")))
			(search-forward-regexp
			 (concat "^\\("
							 "[^>]\\{2\\}.*" ; do not search in quoted messages (yes, [^>] has to appear twice here)
							 "\\(" (string-join keywords "\\|") "\\)"
							 "\\|"
							 "\\(" (string-join keywords "\\|") "\\)"
							 "\\)")
			 nil t))))

(defun tl/mu4e-check-attachment ()
	"Check whether the message has an attachment if it mentions one."
	(when (and (tl/mu4e-message-mentions-attachment-p)
						 (not (tl/mu4e-message-has-attachment-p)))
		(when (yes-or-no-p "Warning: Attachement is mentioned, but not yet added! Do you want to add an attachment?")
			(error "Please add an attachment before sending the email!")
			)))

(add-hook 'message-send-hook
          'tl/mu4e-check-attachment)

Check whether the message has a subject:

(defun tl/mu4e-check-empty-subject ()
	"Check whether the message has a subject."
  (let ((subject (message-field-value "Subject")))
    (when (or (not subject) 
							(string-empty-p subject))
      (message "Warning: Subject is missing! Please provide a subject before sending.")
      (sit-for 2) ; Display the warning for 2 seconds
			(message-goto-subject)
      (error "Cannot send the email without a subject"))))

(add-hook 'message-send-hook
          'tl/mu4e-check-empty-subject)
Templating

For re-using predefined text snippets, tempel can be used. See templates/tempel/templates for an example.

yasnippet is not recommended when the snippet directly changes the buffer using functions (which is what I want).

A simpler method might be abbrevs, which is built-in in Emacs,

For specifying templates of whole emails, there is no straightforward solution as far as I know.

Formatting

format=flowed is a general technique to give mail clients the flexibility to reformat parts of a message, particularly by inserting soft line breaks when paragraphs are filled. With this, the receiving email client will later be able to distinguish “deliberate”/hard from “automatic”/soft line breaks. The motivation ist that using hard line breaks when filling paragraphs will likely mess up the display of quoted text in downstream email clients.

Mu4e has built-in support of format=flowed, see https://www.djcbsoftware.nl/code/mu/mu4e/Writing-messages.html#How-can-I-apply-format_003dflowed-to-my-outgoing-messages_003f.

;; Activate format=flowed (i.e. the automatic insertion of soft line breaks).
(setq mu4e-compose-format-flowed t)

Notice that the Emacs MIME library also knows format=flowed, see https://www.gnu.org/software//emacs/manual/html_node/emacs-mime/Flowed-text.html.

;; Column beyond which format=flowed lines are wrapped, in outgoing messages.
;; The variable controls how the text will look in a client that does not support flowed text, the default is to wrap after 66 characters.
(setq fill-flowed-encode-column 66)

There is one nasty detail though: Mu4e will only apply format=flowed, if use-hard-newlines is enabled, in which case a hard line break will be inserted whenever return is pressed (using the function newline or open-line). This is fine when writing text directly in the compose buffer. However, if one yanks text (or expands a template), the visible line breaks will be underspecified, and therefore interpreted as soft line breaks if there are any hard line breaks inserted with return. This can result in a great mess, because empty lines are potentially not interpreted as separators between paragraphs and the visible structure will be lost.

My solution is to harden all manually inserted or pasted line breaks at the very end when sending the message. Quoted messages do not get hard line breaks (this obviously would make format=flowed useless) with two exceptions:

  • the header of quoted messages, which start with box symbols (┌│└)
  • lines in quotes which start with two consecutive spaces after the quote symbols

This will, for example, avoid breaking Org tables, if they are preceded by at least two consecutive spaces.

(add-hook 'message-send-hook
          (defun format-message-before-sending ()
						(save-excursion
							(message-goto-body)
							(delete-trailing-whitespace)
							;; Harden line breaks except most quoted ones
							(while (search-forward-regexp "^\\([^>]\\|>+[ ][ ┌│└]\\).*\n"  nil t)
								(put-text-property (1- (point)) (point) 'hard t))
							;; Harden further line breaks and activate use-hard-newlines
							(use-hard-newlines 1 t))))

Perhaps obsolete: In mu4e, line breaks are handled in a way that is very unusual when coming from, e.g., Thunderbird. Namely, (hard) line breaks are ignored if they are preceded by trailing whitespace, which may lead to an unexpected paragraph structure in the sent message. (This is probably an effect of using format=flowed above.) In order to preserve the line breaks that one intentionally makes when writing the message, the following code therefore adds delete-trailing-whitespace to message-send-hook:

(add-hook 'message-send-hook
          'delete-trailing-whitespace)
Calendar invitations
  • State “TODO” from [2023-12-15 Fri 09:31]
Citations

Citation lines precede cited contents of replied or forwarded messages. Out of the box, Mu4e only includes the sender, which can be adjusted with message-citation-line-format:

;; Alternatively, see https://www.djcbsoftware.nl/code/mu/mu4e/Writing-messages.html
(setq  message-citation-line-format "On %Y-%m-%d at %H:%M:%S, %f wrote:"
			 message-citation-line-function 'message-insert-formatted-citation-line)

For adding further fields of a message to the citation line, I replace a special citation line with the output of the function tl/message-citation-line-string using mu4e-compose-mode-hook (see above) :

(setq message-citation-line-format "MU4E: REPLACE THIS CITATION LINE!")
(defun tl/mail-address-plist-to-string (address)
  "Convert the plist ADDRESS with keys :name and :email into a string."
	(let ((email (plist-get address ':email))
				(name  (plist-get address ':name)))
		(concat
		 (or name email)
		 " "
		 "<" email ">")))

(defun tl/message-citation-line-string ()
	"Generate citation text for current message with mu4e functions."
	(let* ((msg mu4e-compose-parent-message)
				 (from (mu4e-message-field msg :from))
				 (to (mu4e-message-field msg :to))
				 (cc (mu4e-message-field msg :cc))
				 (subject (mu4e-message-field msg :subject))
				 (date (mu4e-message-field msg :date)))
		(concat
		 "\n"
		 "> ┌ \n"
		 "> │ From: " (mapconcat 'tl/mail-address-plist-to-string from ", ") "\n"
		 "> │ Subject: " (save-match-data
											 (string-replace "\n" "\n> │          "
																			 (string-fill subject (- message-fill-column 15)))) "\n"
		 "> │ To: " (mapconcat 'tl/mail-address-plist-to-string to ",\n> │     ") "\n"
		 (when cc (concat "> │ Cc: " (mapconcat 'tl/mail-address-plist-to-string cc ",\n> │     ") "\n"))
		 "> │ Date: " (format-time-string "%A, %Y-%m-%d, at %H:%M" date) "\n"
		 "> └ \n"
		 ">"
		 )))

Since v1.12, citation lines of forwarded messages should be changed using Gnus’ message-forward:

(setq message-forward-included-headers '("^From:" "^Subject:" "^To:" "^Cc:" "^Date:"))

By default, mu4e removes the signature from cited messages by using message-cite-original-without-signature as value of mu4e-compose-cite-function. To include the signature, uncomment the following:

;; (setq mu4e-compose-cite-function 'message-cite-original)

Taken from djcb/mu#1843, but should be fixed in upcomping versions of mu4e. Remove buttons like [[S/MIME ... from text bodies of cited messages:

(defun tv/mu4e-remove-buttons-in-reply (original-fn &rest args)
  (if current-prefix-arg
      (delete-region (point) (point-max))
    (save-excursion
      (message-goto-body)
      (while (re-search-forward "^[[]\\{2\\}.*[]]\\{2\\}" nil t)
        (replace-match "")))
    (apply original-fn args)))
(add-function :around mu4e-compose-cite-function #'tv/mu4e-remove-buttons-in-reply)
Mailing lists

I use helm-khard for this. The steps are:

  • Make sure that categories are shown in helm-khard.
  • The contacts of the mailing list are assign a specific category.
  • When writing an email, contacts are selected by filtering for this category.
Signing and encryption

Signing and encryption (vice versa verification and decryption) is done using emacs-mime, and can therefore be selected in the attachment menu.

Documentation:

Global settings:

(setq mml-smime-use 'epg ; Use EasyPG (epg), i.e. GnuPG, when handling S/MIME.
			mm-sign-option nil ; With 'guided, the key can be chosen in a menu.
			mm-verify-option 'always	 ; Alwyays verify digital signatures. 
			gnus-buttonized-mime-types '("multipart/signed") ; Required by 'mm-verify-option' being set to 'always.
			)

Account-local settings:

(setq  mml-secure-smime-sign-with-sender t ; If t, use message sender to find an S/MIME key to sign with.
			 mml-default-sign-method "smime"		 ; "smime" or "pgpmime"
			 epg-user-id "123456789"						 ; GnuPG ID of your default identity when using PGP.
			 smime-keys nil											 ; Map mail addresses to a PEM file containing Certificate (and private key).
			 smime-CA-directory nil							 ; Directory containing certificates (in PEM format) for CAs you trust.
			 smime-CA-file nil									 ; The same as `smime-CA-directory', but in one PEM file.  
			 )

Caveats:

Support of DKIM

  • Show status as header field using opendkim (pretty slow): djcb/mu#2348
Send messages
(setq send-mail-function 'smtpmail-send-it
			smtpmail-debug-info t)

Normally, smtpmail will try first to send emails via SMTP without user/password credentials, and then retry using credentials if the server says that it requires it. Some mail servers do not like this and will therefore reject these requests. This can be changed by setting smtpmail-servers-requiring-authorization to the address of the SMTP server. As a default, the following enforces authentification for all SMTP servers.

(setq smtpmail-servers-requiring-authorization ".*")
Search messages

There is a built-in search, but I prefer helm-mu, which allows for incremental search of mails and contacts.

Bookmarks

Queries can be stored as bookmarks: https://www.djcbsoftware.nl/code/mu/mu4e/Bookmarks.html

My bookmarks are narrowed to helm-mu-default-search-string, which makes it necessary to set the bookmarks every time mu4e-main-mode-hook is called (and helm-mu-default-search-string is possibly changed).

(setq mu4e-bookmarks nil)								; Remove any default bookmarks
(add-hook 'mu4e-main-mode-hook
					(lambda ()
						(setq mu4e-bookmarks
									`((:name "With attachment"
													 :key ?a
													 :query ,(concat helm-mu-default-search-string " flag:attach"))
										(:name "With calendar invitation"
													 :key ?c
													 :query ,(concat helm-mu-default-search-string " flag:calendar"))
										(:name "With images"
													 :query ,(concat helm-mu-default-search-string " mime:image/*")
													 :key ?i)
										(:name "Flagged"
													 :key ?f
													 :query ,(concat helm-mu-default-search-string " flag:flagged"))
										(:name "Unread messages"
													 :query ,(concat helm-mu-default-search-string " flag:unread AND NOT flag:trashed")
													 :key ?u)
										(:name "Today's messages"
													 :query ,(concat helm-mu-default-search-string " date:today..now")
													 :key ?t)
										(:name "Last 7 days"
													 :query ,(concat helm-mu-default-search-string " date:7d..now")
													 :hide-unread t
													 :key ?w)
										(:name "[Global] With attachment"
													 :key ?A
													 :query "flag:attach")
										(:name "[Global] With calendar invitation"
													 :key ?C
													 :query "flag:calendar")
										(:name "[Global] Messages with images"
													 :query "mime:image/*"
													 :key ?I)
										(:name "[Global] Flagged"
													 :key ?F
													 :query "flag:flagged")
										(:name "[Global] Unread messages"
													 :query "flag:unread AND NOT flag:trashed"
													 :key ?U)
										(:name "[Global] Today's messages"
													 :query "date:today..now"
													 :key ?T)
										(:name "[Global] Last 7 days"
													 :query "date:7d..now"
													 :hide-unread t
													 :key ?W)
										)
									)))
Export messages

Note: As of v1.12.6, mu4e comes with a function mu4e-save-message.

The following function (written by Dirk-Jan Binnema, copied from https://notes.baty.net/notes/saving-a-maildir-email-as-.eml-using-mu4e/) exports an email at point to an eml file.

(defun djcb-mu4e-copy-message-at-point (&optional dir)
  "Copy message at point to somewhere else as <date>_<subject>.eml."
  (interactive)
  (let* ((msg (mu4e-message-at-point))
         (target (format "%s_%s.eml"
                         (format-time-string "%F" (mu4e-message-field msg :date))
                         (or (mu4e-message-field msg :subject) "No subject"))))
    (copy-file
     (mu4e-message-field msg :path)
     (format "%s/%s" (or dir (read-directory-name "Copy message to: ")) target) 1)))

Let’s write a proper mu4e action based on this:

(defun tl/mu4e-action-copy-message-file (msg)
  "Copy message to <date>_<subject>.eml in some user-specified
directory."
  (interactive)
	(let ((target
				 (string-replace "/" "-"
												 (format "%s_%s.eml"
																 (format-time-string "%F" (mu4e-message-field msg :date))
																 (or (mu4e-message-field msg :subject) "No subject")))))
		(copy-file
		 (mu4e-message-field msg :path)
		 (format "%s/%s" (read-directory-name "Copy message to: " (expand-file-name "~/")) target) 1)))
Linking messages

Documentation: https://www.djcbsoftware.nl/code/mu/mu4e/Org_002dmode-links.html

Load org-mode integration (FIXME: necessary?):

(require 'mu4e-org)

Store link to message if in header view, not to header query:

(setq org-mu4e-link-query-in-headers-mode nil)

Adapt description of links:

(defun tl/mu4e-org-link-descr (msg)
  (let ((subject (or (plist-get msg :subject)
                     "No subject"))
        (date (or (format-time-string mu4e-headers-date-format
																			(mu4e-msg-field msg :date))
                  "No date"))
				(sender (or (plist-get (car (plist-get msg :from)) :name)
										"No Sender")))
    (concat date " | " sender " | " subject)))

(setq mu4e-org-link-desc-func 'tl/mu4e-org-link-descr)

Open links to messages in the mu4e frame:

See the discussion here: https://groups.google.com/g/mu-discuss/c/EME00itASYM/m/hu0faSFSBwAJ I choose a different approach, namely modifying the function mu4e-org-open.

(defun mu4e-org-open (link)
  "Open the org LINK.
Open the mu4e message (for links starting with \"msgid:\") or run
the query (for links starting with \"query:\")."
  (require 'mu4e)
	;; BEGIN: added by TL
	;; Swich to frame with buffer "*mu4e-headers*"
  (let* ((buffer-name mu4e-headers-buffer-name)
				 (buffer (get-buffer buffer-name)))
    (if buffer
        (let ((frames (frame-list)))
            (dolist (frame frames)
              (when (get-buffer-window buffer frame)
                (select-frame-set-input-focus frame))))
			(progn
				(message (format "No buffer named %s; creating new frame to open link" buffer-name))
				(make-frame))))
	;; END: added by TL
  (cond
   ((string-match "^msgid:\\(.+\\)" link)
    (mu4e-view-message-with-message-id (match-string 1 link)))
   ((string-match "^query:\\(.+\\)" link)
    (mu4e-search (match-string 1 link) current-prefix-arg))
   (t (mu4e-error "Unrecognized link type '%s'" link))))
Open mailto links

To open mailto URLs in mu4e, do the following (taken from https://emacs.stackexchange.com/questions/52281/handling-mailto-links-with-mu4e):

  • Set mail-user-agent to #'mu4e-user-agent.
(setq mail-user-agent #'mu4e-user-agent
			message-mail-user-agent t  ; when t, use MUA specified in mail-user-agent
)
[Desktop Entry]
Name=Emacs client for mailto URLs
Comment=Open mailto URL in Emacs
MimeType=x-scheme-handler/mailto;
## From the original Emacs desktop file (https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/emacsclient-mail.desktop)
# Exec=emacsclient --alternate-editor= --eval "(message-mailto (pop server-eval-args-left))" %u
## My adaptation of the above
## For some unkown reason, there can be a trailing apostrophe. 
Exec=emacsclient --alternate-editor= --eval "(message-mailto (string-trim-right \"%u\" \"'\"))"
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs
  • Select this as default programm to open mailto URLs in your browser.
  • Optionally add the desktop file to ~/.config/mimeapps.list.
[Default Applications]
x-scheme-handler/mailto=emacsclient-mailto.desktop
Capture messages

One can store messages using Org’s capture mechanism. See also the dedicated page in the documentation of mu4e: https://www.djcbsoftware.nl/code/mu/mu4e/Org_002dmode-links.html

I mainly use this for scheduling when to work on messages. For this, I add a suitable template to org-capture-templates, which will create a header with a link in my capture file:

(add-to-list 'org-capture-templates
						 '("s" "Scheduled emails" entry
							 (file+headline (lambda nil
																(expand-file-name "captures.org" org-directory))
															"Scheduled emails")
							 "* %?%a\nSCHEDULED: %^T" :prepend t))

Let’s also add keybindings for capturing messages:

(define-key mu4e-headers-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture)
(define-key mu4e-view-mode-map    (kbd "C-c c") 'mu4e-org-store-and-capture)

Chat

Mastodon

mastodon.el

  • State “TODO” from [2024-10-23 Wed 08:40]

https://codeberg.org/martianh/mastodon.el

A Mastodon client for Emacs.

(use-package mastodon
  :ensure t    
	:config
  ;; (mastodon-discover)
	;;; Account settings should be put elsewhere
	(setq mastodon-instance-url "https://social.instance.org"
				mastodon-active-user "example_user")
	(setq mastodon-tl--show-avatars t
				mastodon-tl--horiz-bar (make-string 70 ?═)
				mastodon-media--avatar-height 40
				mastodon-media--preview-max-height 500
				mastodon-tl--highlight-current-toot nil)
	;;; Recentering the view like this will throw "apply: Wrong number of arguments: (0 . 1), 2"	
	;; (advice-add 'mastodon-tl--goto-prev-item
	;; 						:after #'recenter)
  ;; (advice-add 'mastodon-tl--goto-next-item
	;; 						:after #'recenter)
	)

A nice alternative design by Nicolas Rougier: https://github.com/rougier/mastodon-alt

  • [ ] Crashes
(use-package mastodon-alt
  :load-path "./lisp/mastodon-alt.el"
  :after mastodon
  :config
	(mastodon-alt-tl-activate)
	;; (advice-add 'mastodon-tl--goto-prev-item
	;; 						:after #'recenter)
  ;; (advice-add 'mastodon-tl--goto-next-item
	;; 						:after #'recenter)
	)

A fitting hydra from https://holgerschurig.github.io/en/emacs-mastodon-hydra/

(defhydra hydra-mastodon (:color blue :hint nil)
  "
Timelines^^   Toots^^^^           Own Toots^^   Profiles^^      Users/Follows^^  Misc^^
^^-----------------^^^^--------------------^^----------^^-------------------^^------^^-----
_H_ome        _n_ext _p_rev       _r_eply       _A_uthors       follo_W_         _X_ lists
_L_ocal       _T_hread of toot^^  wri_t_e       user _P_rofile  _N_otifications  f_I_lter
_F_ederated   (un) _b_oost^^      _e_dit        ^^              _R_equests       _C_opy URL
fa_V_orites   (un) _f_avorite^^   _d_elete      _O_wn           su_G_estions     _s_earch
_#_ tagged    (un) p_i_n^^        ^^            _U_pdate own    _M_ute user      _h_elp
_@_ mentions  (un) boo_k_mark^^   show _E_dits  ^^              _B_lock user
boo_K_marks   _v_ote^^
trendin_g_
_u_pdate
"
  ("H" mastodon-tl--get-home-timeline)
  ("L" mastodon-tl--get-local-timeline)
  ("F" mastodon-tl--get-federated-timeline)
  ("V" mastodon-profile--view-favourites)
  ("#" mastodon-tl--get-tag-timeline)
  ("@" mastodon-notifications--get-mentions)
  ("K" mastodon-profile--view-bookmarks)
  ("g" mastodon-search--trending-tags)
  ("u" mastodon-tl--update :exit nil)

  ("n" mastodon-tl--goto-next-toot)
  ("p" mastodon-tl--goto-prev-toot)
  ("T" mastodon-tl--thread)
  ("b" mastodon-toot--toggle-boost :exit nil)
  ("f" mastodon-toot--toggle-favourite :exit nil)
  ("i" mastodon-toot--pin-toot-toggle :exit nil)
  ("k" mastodon-toot--bookmark-toot-toggle :exit nil)
  ("c" mastodon-tl--toggle-spoiler-text-in-toot)
  ("v" mastodon-tl--poll-vote)

  ("A" mastodon-profile--get-toot-author)
  ("P" mastodon-profile--show-user)
  ("O" mastodon-profile--my-profile)
  ("U" mastodon-profile--update-user-profile-note)

  ("W" mastodon-tl--follow-user)
  ("N" mastodon-notifications-get)
  ("R" mastodon-profile--view-follow-requests)
  ("G" mastodon-tl--get-follow-suggestions)
  ("M" mastodon-tl--mute-user)
  ("B" mastodon-tl--block-user)

  ("r" mastodon-toot--reply)
  ("t" mastodon-toot)
  ("e" mastodon-toot--edit-toot-at-point)
  ("d" mastodon-toot--delete-toot)
  ("E" mastodon-toot--view-toot-edits)

  ("I" mastodon-tl--view-filters)
  ("X" mastodon-tl--view-lists)
  ("C" mastodon-toot--copy-toot-url)
  ("s" mastodon-search--search)
  ("h" describe-mode)

  ("q" doom/escape))

RSS/Atom feed reader

elfeed

  • State “TODO” from [2024-11-10 Sun 20:06]

https://github.com/skeeto/elfeed

Web feed reader for Emacs.

Issues:

  • [ ] Rework hydra
(use-package elfeed
  :ensure t
  :bind (:map elfeed-search-mode-map
							("m" . elfeed-toggle-star)
							("M" . tl/elfeed-show-starred)
							("R" . elfeed-mark-all-as-read)
							("U" . tl/elfeed-show-unread)
							("s" . tl/elfeed-search-live-filter-space)
							("f" . tl/elfeed-search-live-filter-space)
							("*" . tl/elfeed-show-starred)
							;; 						;; ("/" . mz/make-and-run-elfeed-hydra)
							("A" . tl/elfeed-show-all)
							)
	:config

	(when (boundp 'my-elfeed-db-directory)
		(setq elfeed-db-directory my-elfeed-db-directory))
	(defalias 'elfeed-toggle-star
		(elfeed-expose #'elfeed-search-toggle-all 'star))

	(defface star-elfeed-entry
		'((t :foreground "#FD5FF0" :weight bold))
		"Face of a starred Elfeed entry.")
	(push '(star star-elfeed-entry)
				elfeed-search-face-alist)

	(defface unread-elfeed-entry
		'((t :inherit font-lock-keyword-face))
		"Face of an unread Elfeed entry.")
	(push '(unread unread-elfeed-entry)
				elfeed-search-face-alist)

	;; TODO: Unfortunately, it does not work like this.
  ;; (set-face-attribute 'elfeed-search-feed-face nil :inherit font-lock-number-face)
	;; (set-face-attribute 'elfeed-search-tag-face nil :inherit 'font-lock-comment-face)

	(defun elfeed-mark-all-as-read ()
		(interactive)
		(mark-whole-buffer)
		(elfeed-search-untag-all-unread))

	(defun tl/elfeed-show-starred ()
		(interactive)
		(elfeed-search-set-filter "@6-months-ago +star"))
	
	(defun tl/elfeed-show-all ()
		(interactive)
		(elfeed-search-set-filter "@6-months-ago"))

	(defun tl/elfeed-show-unread ()
		(interactive)
		(elfeed-search-set-filter "@6-months-ago +unread"))

	(defun tl/elfeed-search-live-filter-space ()
		"Insert trailing space when running elfeed filter"
		(interactive)
		(let ((elfeed-search-filter (concat elfeed-search-filter " ")))
			(elfeed-search-live-filter)))	
	
	(advice-add 'elfeed :after
							;; (add-hook 'elfeed-search-mode-hook
							#'(lambda ()
									(with-current-buffer "*elfeed-search*"
										(toggle-truncate-lines t))))

	;; Generate filter hydra for a given set of tags (using code macros).
	;; Taken from http://cestlaz.github.io/posts/using-emacs-31-elfeed-3/.

	(defun z/hasCap (s) 
		""
		(let ((case-fold-search nil))
      (string-match-p "[[:upper:]]" s)
      ))

	(defun z/get-hydra-option-key (s)
		"returns single upper case letter (converted to lower) or first"
		(interactive)
		(let ( (loc (z/hasCap s)))
			(if loc
					(downcase (substring s loc (+ loc 1)))
				(substring s 0 1)
				)))

	(defun mz/make-elfeed-cats (tags)
		"Returns a list of lists. Each one is line for the hydra configuratio in the form
     (c function hint)"
		(interactive)
		(mapcar (lambda (tag)
							(let* (
										 (tagstring (symbol-name tag))
										 (c (z/get-hydra-option-key tagstring))
										 )
								(list c (append '(elfeed-search-set-filter) (list (format "@6-months-ago +%s" tagstring) ))tagstring  )))
						tags))

	(defmacro mz/make-elfeed-hydra ()
		`(defhydra mz/hydra-elfeed ()
			 "filter"
			 ,@(mz/make-elfeed-cats (elfeed-db-get-all-tags))
			 ("*" (elfeed-search-set-filter "@6-months-ago +star") "Starred")
			 ;; ("M" elfeed-toggle-star "Mark")
			 ("A" (elfeed-search-set-filter "@6-months-ago") "All")
			 ("T" (elfeed-search-set-filter "@1-day-ago") "Today")
			 ("Q" bjm/elfeed-save-db-and-bury "Quit Elfeed" :color blue)
			 ("q" nil "quit" :color blue)
			 ))

	(defun mz/make-and-run-elfeed-hydra ()
		""
		(interactive)
		(mz/make-elfeed-hydra)
		(mz/hydra-elfeed/body))

	;; Write to disk when quiting
	;; TODO: Still needed?
	(defun bjm/elfeed-save-db-and-bury ()
		"Wrapper to save the elfeed db to disk before burying buffer"
		(interactive)
		(elfeed-db-save)
		(quit-window))

	)

Change the columns (and some faces) of the elfeed-search buffer:

(defun tl/elfeed-search-print-entry (entry)
  "Print ENTRY to the buffer."
  (let* ((date (elfeed-search-format-date (elfeed-entry-date entry)))
         (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
         (title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
         (feed (elfeed-entry-feed entry))
         (feed-title
          (when feed
            (or (elfeed-meta feed :title) (elfeed-feed-title feed))))
				 (feed-title-max-width 20)
				 (feed-short-title
					(if (> (length feed-title) (- feed-title-max-width 1))
							(concat (truncate-string-to-width feed-title
																								(- feed-title-max-width 1))              
											"")
						feed-title))
				 (feed-title-column (elfeed-format-column
														 feed-short-title (elfeed-clamp
																							 feed-title-max-width
																							 feed-title-max-width
																							 feed-title-max-width)
														 :left))
				 (elfeed-search-trailing-width (+ 11 feed-title-max-width 20))
         (title-width (- (window-width) 10 elfeed-search-trailing-width))
				 (elfeed-search-title-max-width
					(or (and (> title-width elfeed-search-title-max-width)
									 title-width)
							elfeed-search-title-max-width))
         (title-column (elfeed-format-column
                        title (elfeed-clamp
                               elfeed-search-title-min-width
                               title-width
                               elfeed-search-title-max-width)
                        :left))
         (tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
         (tags-str (mapconcat
                    (lambda (s) (propertize s 'face 'elfeed-search-tag-face))
                    tags ","))
				 )
    (insert (propertize date 'face 'elfeed-search-date-face) " ")
		(insert (propertize feed-title-column 'face 'font-lock-number-face) " ") ; Only hard-coding the face seems to work.
		;; (when feed-title
		;; 	(insert (propertize feed-title 'face 'elfeed-search-feed-face) " "))
		(insert (propertize title-column 'face title-faces 'kbd-help title) " ")
		(when tags
			(insert (propertize (concat "(" tags-str ")") 'face 'font-lock-comment-face))) ; Only hard-coding the face seems to work.
		;; (when tags
		;; 	(insert "(" tags-str ")"))
		))
(setq elfeed-search-print-entry-function #'tl/elfeed-search-print-entry)

Wrapper function for elfeed (used in hydra-f7):

(defun tl/elfeed ()
	(interactive)
	(elfeed)
	(visual-line-mode -1))

Split-pane view for Elfeed, taken from https://karthinks.com/software/lazy-elfeed/.

Issues:

  • [ ] Killing the elfeed-show buffer replaces it with a new elfeed-search window. Intended behavior: Return to original elfeed-search window and “unsplit” it.
(setq elfeed-show-entry-switch #'elfeed-display-buffer)
(defun elfeed-display-buffer (buf &optional act)
	(pop-to-buffer buf)
	(set-window-text-height (get-buffer-window) (round (* 0.7 (frame-height)))))

(defun elfeed-search-show-entry-pre (&optional lines)
	"Returns a function to scroll forward or back in the Elfeed
  search results, displaying entries without switching to them."
  (lambda (times)
    (interactive "p")
    (forward-line (* times (or lines 0)))
    (recenter)
    (call-interactively #'elfeed-search-show-entry)
    (select-window (previous-window))
    (unless elfeed-search-remain-on-entry (forward-line -1))))

(with-eval-after-load 'elfeed
  (define-key elfeed-search-mode-map (kbd "n") (elfeed-search-show-entry-pre +1))
  (define-key elfeed-search-mode-map (kbd "p") (elfeed-search-show-entry-pre -1))
  (define-key elfeed-search-mode-map (kbd "M-RET") (elfeed-search-show-entry-pre)))

The following code allows one to watch YouTube videos with mpv. Taken from https://joshrollinswrites.com/help-desk-head-desk/20200611/.

(require 'elfeed)

(defun elfeed-v-mpv (url)
  "Watch a video from URL in MPV"
  (async-shell-command (format "mpv %s" url)))

(defun elfeed-view-mpv (&optional use-generic-p)
  "Youtube-feed link"
  (interactive "P")
  (let ((entries (elfeed-search-selected)))
    (cl-loop for entry in entries
	     do (elfeed-untag entry 'unread)
	     when (elfeed-entry-link entry)
	     do (elfeed-v-mpv it))
    (mapc #'elfeed-search-update-entry entries)
    (unless (use-region-p) (forward-line))))

(define-key elfeed-search-mode-map (kbd "v") 'elfeed-view-mpv)

helm-elfeed

!!! WORK IN PROGRESS !!!

https://codeberg.org/timmli/helm-elfeed

A Helm interface for Elfeed.

(use-package helm-elfeed
	:after elfeed
  :load-path "lisp/helm-elfeed/"
	:config
	(define-key elfeed-search-mode-map (kbd "C-i") 'helm-elfeed)
)

elfeed-goodies

https://github.com/jeetelongname/elfeed-goodies

From the documentation:

  • An adaptive, powerline-based header for the elfeed-search and elfeed-entry buffers, with a matching entry format.
  • Split pane setup.
  • A function to toggle the elfeed-log buffer in a popup window.
  • Easy customisation.

Issues (therefore disabled right now)

(use-package elfeed-goodies
	:ensure t
	:after elfeed
	:config
	(elfeed-goodies/setup)
	(setq elfeed-goodies/entry-pane-size 0.75
				elfeed-goodies/entry-pane-position 'bottom))

elfeed-org

https://github.com/remyhonig/elfeed-org

Configure the Elfeed RSS reader with an Org file, the path of which is specified in my-elfeed-org-file. Feed collections can be converted to the format of elfeed-org with the function elfeed-org-import-opml.

	(use-package elfeed-org
		:ensure t
		:after elfeed
		:config
		(elfeed-org)
		(when (boundp 'my-elfeed-org-file)
			(setq rmh-elfeed-org-files (list my-elfeed-org-file)))
		(setq rmh-elfeed-org-auto-ignore-invalid-feeds nil)) ; handle with care

elfeed-summary

  • State “TODO” from [2022-05-15 Sun 10:53]

https://github.com/SqrtMinusOne/elfeed-summary

A tree-based feed summary interface for elfeed. Superseded (in my view) by helm-elfeed.

Issues & TODOs:

  • [ ] Add hydra
  • [ ] Fold all groups in the beginning
  • [ ] Change group face if it contains unread entities
(use-package elfeed-summary
	:ensure t
  :pin MELPA
	:config
  :init
	(setq elfeed-summary-settings
				(append
				 '(
					 ;; (group
					 ;; (:title . "All feeds")
					 ;; (:elements
					 ;; (query . :all)))
					 (group (:title . "Searches")
									(:elements
									 (search
										(:filter . "+star")
										(:title . "Starred/marked entries"))
									 (search
										(:filter . "@14-days-ago +unread")
										(:title . "Unread entries this and last week"))))
					 (auto-tags (:max-level . 2)
											(:original-order . t))
					 ;; (tag-groups (:repeat-feeds . t))
					 ;; (mapcar #'(lambda (tag)
					 ;; 						 `(group
					 ;; 							 (:title . ,(upcase-initials (symbol-name tag)))
					 ;; 							 (:elements (query . (,tag)))))
					 ;; 				 (elfeed-db-get-all-tags))
					 )))
	:bind (:map elfeed-summary-mode-map
							("<C-tab>" . iflipb-next-buffer)
							("<C-iso-lefttab>" . iflipb-previous-buffer)
							("C-c C-u" . elfeed-summary-update)
							("U" . elfeed-summary-update)
							("R" . elfeed-summary--action-mark-read))
	:custom-face
	(elfeed-summary-count-face-unread ((t (:inherit unread-elfeed-entry))))
	)

Wrapper function for starting elfeed-summary with hydra-f7.

(defun tl/elfeed-summary ()
	(interactive)
	(elfeed) ; This is necessary for truncating lines. 
	(elfeed-summary)
	(with-current-buffer "*elfeed-summary*"
		(forward-line +1)
		(magit-section-forward)
		(forward-line +1)))

elfeed-tube

https://github.com/karthink/elfeed-tube

Elfeed interface to Youtube subscriptions.

(use-package elfeed-tube
  :straight (:host github :repo "karthink/elfeed-tube")
  :after elfeed
  :demand t
  :config
  ;; (setq elfeed-tube-auto-save-p nil) ;; t is auto-save (not default)
  ;; (setq elfeed-tube-auto-fetch-p t) ;;  t is auto-fetch (default)
  (elfeed-tube-setup)

  :bind (:map elfeed-show-mode-map
         ("F" . elfeed-tube-fetch)
         ([remap save-buffer] . elfeed-tube-save)
         :map elfeed-search-mode-map
         ("F" . elfeed-tube-fetch)
         ([remap save-buffer] . elfeed-tube-save)))

Slack

Viewing (and editing)

CoNNLL-U

https://github.com/odanoburu/conllu-mode

Major mode for CoNLL-U files.

(use-package conllu-mode
	:ensure t
	:config
	(add-to-list 'auto-mode-alist '("\\.conllu\\'" . conllu-mode))
)

CSV/TSV

csv-mode

https://elpa.gnu.org/packages/csv-mode.html

Major mode for editing files in a generalized CSV (character-separated values) format.

(use-package csv-mode
	:ensure t)

EPUB reader

https://github.com/wasamasa/nov.el

PDF

doc-view

(when (eq system-type 'windows-nt)
	(setq doc-view-ghostscript-program "gswin64c.exe"))

pdf-tools

The hard part is to generate epdfinfo.exe under Windows, which is why I’m not using it there.

New: https://github.com/vedang/pdf-tools Old: https://github.com/politza/pdf-tools

Issues

(use-package pdf-tools
	:ensure t
	:pin MELPA
	;; :if (executable-find "epdfinfo")
	:mode (("\\.pdf\\'" . pdf-view-mode))
	:bind ("M-w" . pdf-view-kill-ring-save) ; does not work
	:config
	(setq-default pdf-view-display-size 'fit-page) ; fit page by default
	(setq pdf-view-resize-factor 1.10
				pdf-view-selection-style 'glyph)
	(pdf-tools-install)
	;; Manually set path to epdfinfo binary
	;; (setq pdf-info-epdfinfo-program (expand-file-name "epdinfo.exe" (concat wemacs-dir "pdftools")))
	(define-key pdf-view-mode-map (kbd "M-w") 'pdf-view-kill-ring-save)
	(define-key pdf-view-mode-map (kbd "C-w") 'pdf-view-kill-ring-save)
	(define-key pdf-view-mode-map (kbd "M-<left>") 'pdf-history-backward)
	(define-key pdf-view-mode-map (kbd "M-<right>") 'pdf-history-forward)
	(add-hook 'pdf-occur-buffer-mode-hook 'next-error-follow-minor-mode) ; activate follow mode in `pdf-occur'
	)
org-pdftools
  • State “TODO” from [2020-03-30 Mo 11:07]

org-pdftools provides =org-links with prefix pdf: to single pages of PDFs using pdf-tools. URL: https://github.com/fuxialexander/org-pdftools

(use-package org-pdftools
	:ensure t
	;; :hook (org-load . org-pdftools-setup-link) ; recommended but does not work
	:hook (org-mode . org-pdftools-setup-link) 
	;; :config
	;; (with-eval-after-load 'org ; Instead of: :hook (org-mode . org-pdftools-setup-link)
	;; 	(org-pdftools-setup-link))
	;; (setq org-pdftools-root-dir user-bibliography-pdf-dir) ; not supported anymore?
	)

Signature

The following function checks the signature of a PDF that is opened in a buffer or under point in dired (taken from ChatGPT). It uses the command line tool pdfsig of the Poppler library:

(defun check-pdf-signature-dwim ()
  "Check the digital signature of a PDF file.
If in Dired, check the PDF file at point. If in a `pdf-view-mode` buffer,
check the currently opened PDF file. Otherwise, prompt for a file."
  (interactive)
  (let* ((file (cond
                ;; If in Dired, use the file at point.
                ((derived-mode-p 'dired-mode) (dired-get-file-for-visit))
                ;; If in a `pdf-view-mode` buffer, use the current file.
                ((eq major-mode 'pdf-view-mode) (buffer-file-name))
                ;; Otherwise, prompt the user for a file.
                (t (read-file-name "Select PDF file: "))))
         (output-buffer "*PDF Signature*"))
    (if (and file (string-match "\\.pdf\\'" file))
        (with-output-to-temp-buffer output-buffer
          (call-process "pdfsig" nil output-buffer nil file)
          (display-buffer output-buffer))
      (message "Not a valid PDF file"))))

I will add this to hydra-f4.

DjVu

http://djvu.sourceforge.net/

Starting from GNU Emacs 26, doc-view supports DjVu documents. Both doc-view and djvu-view depend on the package djvulibre, which includes djvused.

Open DjVu files with doc-view or djvu.

(add-to-list 'auto-mode-alist '("\\.djvu\\'" . doc-view-mode))
;; (add-to-list 'auto-mode-alist '("\\.djvu\\'" . djvu-dummy-mode))

Issues with doc-view:

  • [ ] Copying text from within DocView is not possible.
  • [ ] Searching within DocView ist not possible

djvu

https://elpa.gnu.org/packages/djvu.html

Package for reading an editing DjVu files.

(use-package djvu
	:ensure t)

Media player

outline-mode

Images

image+ (ARCHIVED)

https://github.com/mhayashi1120/Emacs-imagex

Show and modify images within emacs. Used in hydra-image.

(use-package image+
	:ensure t
	:init
	(eval-after-load 'image '(require 'image+))
	:config
	(if (eq system-type 'windows-nt)
			(setq imagex-convert-command "magick")  ; "convert" does not work on Windows machines
		(setq imagex-convert-command "convert"))
	)

OSM

https://github.com/minad/osm

OpenStreetMap viewer for Emacs.

(use-package osm
	:ensure t
	:pin MELPA

	;; :bind (("C-c m h" . osm-home)
  ;;        ("C-c m s" . osm-search)
  ;;        ("C-c m v" . osm-server)
  ;;        ("C-c m t" . osm-goto)
  ;;        ("C-c m j" . osm-bookmark-jump))

	:custom
  ;; Take a look at the customization group `osm' for more options.
  (osm-server 'default) ;; Configure the tile server
  (osm-copyright t)     ;; Display the copyright information
	:config
	(add-hook 'osm-mode-hook #'(lambda () (visual-line-mode -1)))
	)

GIF screencast

https://gitlab.com/ambrevar/emacs-gif-screencast

Slim GIF screencasts for Emacs. This package uses scrot, convert and Gifsicle.

(use-package gif-screencast
	:ensure t)

Minor modes

pandoc-mode

pandoc-mode allows one to use pandoc from within Emacs. pandoc is a very rich transducer for text formats. Note that pandoc needs to be installed separately.

(use-package pandoc-mode
	:ensure t
  :config
  ;; (add-hook 'markdown-mode-hook 'pandoc-mode)
  ;; (add-hook 'org-mode-hook 'pandoc-mode)
  (add-hook 'pandoc-mode-hook 'pandoc-load-default-settings))

so-long-mode

https://www.emacswiki.org/emacs/SoLong

so-long.el optimizes the performance on files with long lines. Built into Emacs since v27.

(when (require 'so-long nil :noerror)
   (global-so-long-mode 1))

Buffer

General configuration

Automatically update buffers when files change:

(global-auto-revert-mode t)

Visible bell:

(setq visible-bell t)
(setq ring-bell-function (lambda ()
                           (invert-face 'mode-line)
                           (run-with-timer 0.05 nil 'invert-face 'mode-line)))

Delete marked text on typing:

(delete-selection-mode t)

Use tabs for indent:

(setq-default tab-width 2)
(setq-default indent-tabs-mode t)

Scrolling:

(setq scroll-step            1
      scroll-conservatively  10000)
;; autoscroll compilation output
(setq compilation-scroll-output t)
;; scroll to the first/last line
(setq scroll-error-top-bottom t)

General buffer actions

Open untitled new buffer:

(defun xah-new-empty-buffer ()
  "Open a new empty buffer.
URL `http://ergoemacs.org/emacs/emacs_new_empty_buffer.html'
Version 2015-06-12"
  (interactive)
  (let ((buf (generate-new-buffer "untitled")))
    (switch-to-buffer buf)
    (funcall (and initial-major-mode))
    (setq buffer-offer-save t)))
(global-set-key (kbd "<f7> <f7>") 'xah-new-empty-buffer)

Kill buffer:

(global-set-key (kbd "M-<f3>") 'kill-this-buffer) ; M-<f4> is bound to killing the application under xfce.

Revert buffer:

(global-set-key (kbd "<f5>") 'revert-buffer)

Switch between buffers

iflipb

https://github.com/jrosdahl/iflipb

Switching between buffers with C-tab:

(use-package iflipb
	:ensure t
	:config
	(setq iflipb-wrap-around t)
	(setq iflipb-ignore-buffers "^ ")     ; include all buffers
	:bind
	("<C-tab>" . iflipb-next-buffer)
	("<C-iso-lefttab>" . iflipb-previous-buffer))

buffer-flip

https://github.com/killdash9/buffer-flip.el

Flip through Emacs buffers Alt-Tab style.

bufler

https://github.com/alphapapa/bufler.el

Show open buffers in a more structured way.

(use-package bufler
	:ensure t
	:pin MELPA)

Fuzzy search in helm.

(use-package helm-bufler
	:ensure t
	:pin MELPA
	:config
	(defun helm-bufler-go ()
		(interactive)
		(helm :sources '(helm-bufler-source))))

Autocomplete

yasnippet

https://github.com/joaotavora/yasnippet

Yet Another Snippet system for Emacs.

(use-package yasnippet
	:ensure t
	:pin MELPA
	:config 
	(yas-global-mode 1)
	(add-to-list 'warning-suppress-types '(yasnippet backquote-change)))

Helper functions for yasnippet:

(defun yas/org-insert-time-stamp (&rest args)
  "Return the string that `org-insert-time-stamp' would insert.
Taken from https://emacs.stackexchange.com/questions/24060/yasnippet-truncates-clipboard-contents"
  (with-temp-buffer
    (apply #'org-insert-time-stamp args)
    (buffer-string)))
(defun yas/org-time-stamp (&rest args)
  "Return the string that `org-time-stamp' would insert.
Similar to `yas/org-insert-time-stamp`"
  (with-temp-buffer
    (apply #'org-time-stamp args)
    (buffer-string)))

company

  • [ ] completion of LaTeX code in AUCTex
  • [ ] completion in org-mode of LaTeX code

General settings

http://company-mode.github.io/

(use-package company
	:ensure t
	:config

	;; some general variables
	(setq company-idle-delay 0.2
				company-minimum-prefix-length 1
				company-selection-wrap-around t ; If enabled, selecting item before first or after last wraps around.
				company-tooltip-flip-when-above t
				;; company-show-numbers t
				company-dabbrev-downcase nil
				company-auto-complete nil
				company-tooltip-align-annotations nil ; When non-nil, align annotations to the right tooltip border.
				company-transformers '(company-sort-by-occurrence))

	;; face
	(set-face-attribute 'company-tooltip nil :inherit 'fixed-pitch) ; Use monospaced font in tooltips.

	;; keys
	(eval-after-load 'company
	  '(progn
	     (define-key company-active-map (kbd "<tab>") 'company-complete-selection)
			 (define-key company-active-map (kbd "<return>") nil)
			 (define-key company-active-map (kbd "RET") nil)
	     ;; (define-key company-active-map (kbd "<tab>") 'company-complete-common)  ; Insert the common part of all candidates.  
	     ;; (define-key company-active-map (kbd "<tab>") 'company-select-next))
			 (define-key company-active-map (kbd "<up>") 'company-select-previous)
			 (define-key company-active-map (kbd "<down>") 'company-select-next)
			 ))
	
  ;; activate global-company-mode
	(add-hook 'after-init-hook 'global-company-mode)
	)

When company is actively used by browsing completion candidates, <return> should execute the selected completion.

;; Taken from https://github.com/company-mode/company-mode/issues/530.
(defun my-company-active-return ()
	(interactive)
	(if (company-explicit-action-p)
			(company-complete)
		(call-interactively
		 (or (key-binding (this-command-keys))
				 (key-binding (kbd "RET")))
		 )))

(define-key company-active-map (kbd "<return>") #'my-company-active-return)
(define-key company-active-map (kbd "RET") #'my-company-active-return)

company-emoji

https://github.com/dunn/company-emoji

company backend for emojis. Recommended to be used with emacs-emojify.

(use-package company-emoji
	:ensure t
	:config
	(add-to-list 'company-backends 'company-emoji)) 

company-flx

https://github.com/PythonNut/company-flx

Add fuzzy matching capabilities with flx:

(use-package company-flx
	:ensure t
	:config
	(with-eval-after-load 'company
		(company-flx-mode +1)))

Math symbols

https://github.com/vspinu/company-math

Add company completions for LaTeX and unicode math symbols.

(use-package company-math
	:ensure t
	:config
	;; (add-to-list 'company-backends 'company-math-symbols-unicode) ; insert unicode symbol
	(add-to-list 'company-backends 'company-math-symbols-latex) ; insert LaTeX code
	)

LaTeX/AUCTeX

https://github.com/alexeyr/company-auctex/

Add company completions for AUCTeX.

(use-package company-auctex
	:ensure t
	:config (company-auctex-init))

RefTeX

https://github.com/TheBB/company-reftex

Add company completions for citations and label references in LaTeX based on RefTeX.

(use-package company-reftex
	:ensure t
	:config
	(add-to-list 'company-backends 'company-reftex-labels) 
	(add-to-list 'company-backends 'company-reftex-citations) 
	)

yasnippet

Execute when all backends added to company-backends.

;; Code from https://emacs.stackexchange.com/a/10520/12336
(defvar company-mode/enable-yas t
	"Enable yasnippet for all backends.")
(defun company-mode/backend-with-yas (backend)
	(if (or (not company-mode/enable-yas)
					(and (listp backend) (member 'company-yasnippet backend)))
			backend
		(append (if (consp backend) backend (list backend))
						'(:with company-yasnippet))))
(setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))

academic-phrases

https://github.com/nashamri/academic-phrases

Choose from a collection of academic phrases.

(use-package academic-phrases
	:ensure t)

tempel

https://github.com/minad/tempel

Another package for writing text templates, which is a little simpler than yasnippet. Templates are stored in templates/tempel/templates

(use-package tempel
	:ensure t

  ;; :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
  ;;        ("M-*" . tempel-insert))

  :init

  ;; Setup completion at point
  (defun tempel-setup-capf ()
    ;; Add the Tempel Capf to `completion-at-point-functions'.
    ;; `tempel-expand' only triggers on exact matches. Alternatively use
    ;; `tempel-complete' if you want to see all matches, but then you
    ;; should also configure `tempel-trigger-prefix', such that Tempel
    ;; does not trigger too often when you don't expect it. NOTE: We add
    ;; `tempel-expand' *before* the main programming mode Capf, such
    ;; that it will be tried first.
    (setq-local completion-at-point-functions
                (cons #'tempel-complete
                      completion-at-point-functions)))

  (add-hook 'conf-mode-hook 'tempel-setup-capf)
  (add-hook 'prog-mode-hook 'tempel-setup-capf)
  (add-hook 'text-mode-hook 'tempel-setup-capf)

  ;; Optionally make the Tempel templates available to Abbrev,
  ;; either locally or globally. `expand-abbrev' is bound to C-x '.
  ;; (add-hook 'prog-mode-hook #'tempel-abbrev-mode)
  ;; (global-tempel-abbrev-mode)

	:config
	(setq tempel-path (expand-file-name "templates/tempel/*" user-emacs-directory))
	;; Require trigger prefix before template name when completing.
  (setq tempel-trigger-prefix nil)

	)

;; Optional: Add tempel-collection.
;; The package is young and doesn't have comprehensive coverage.
;; (use-package tempel-collection)

;; Optional: Use the Corfu completion UI
;; (use-package corfu
;;   :init
;;   (global-corfu-mode))

Syntax checking

flymake

https://www.gnu.org/software/emacs/manual/html_node/emacs/Flymake.html

Flymake is a built-in syntax checker for many programming languages. I’m currently using flycheck.

flycheck

https://www.flycheck.org/en/latest/

Syntax checking extension for Emacs, intended as a replacement of Flymake. Additional syntax checking programs for the supported programming languages are required for Flycheck to work.

(use-package flycheck
	:ensure t
	:config
	(global-flycheck-mode t)
	(add-hook 'text-mode-hook #'(lambda ()(flycheck-mode 0)))  ; See flycheck-vale
	)

flycheck-plantuml

https://github.com/alexmurray/flycheck-plantuml

Flycheck support of PlantUML code.

(use-package flycheck-plantuml
  :ensure t
  :after (flycheck plantuml)
  :config (flycheck-plantuml-setup))

Spell checking

https://www.gnu.org/software/emacs/manual/html_node/emacs/Spelling.html

ispell

https://www.gnu.org/software/emacs/manual/html_node/emacs/Spelling.html

ispell is the built-in spell checking routine.

Tell ispell which back-end to use:

(setq ispell-program-name "aspell")

Comparisons of spell checkers:

Note on aspell:

  • Unfortunately, ispell-program-name does not allow for also specifying aspell options. This would be desirable for changing, e.g., home-dir.
  • Newer versions of aspell do not have the CL option home-dir any more. Instead, a configuration file .aspell.conf in the home directory should be used.

The following sets the home-dir option via .aspell.conf:

(if (and (file-exists-p "~/.aspell.conf")
				 personal-dictionary-dir)
		(with-temp-buffer
			(insert (concat "home-dir " personal-dictionary-dir))
			(write-region (point-min) (point-max) "~/.aspell.conf")))

flyspell

https://www.gnu.org/software/emacs/manual/html_node/emacs/Spelling.html

Built-in on-the-fly spell checking.

;; remove keybindings for autocorrect 
(eval-after-load "flyspell"
	'(define-key flyspell-mode-map (kbd "C-;") nil))
(eval-after-load "flyspell"
	'(define-key flyspell-mode-map (kbd "C-.") nil))

;; ;; activate for text
;; (dolist (hook '(text-mode-hook LaTeX-mode-hook))
;; 	(add-hook hook (lambda () (flyspell-mode 1))))

(defun flyspell-toggle (arg)
	(interactive "p")
	(if (bound-and-true-p flyspell-mode)
			(progn
				 (flyspell-mode -1)
			)	 
		(progn
			(flyspell-buffer)
			(flyspell-mode)
			)))

;; move point to previous error
;; http://emacs.stackexchange.com/a/14912/2017
(defun flyspell-goto-previous-error (arg)
  "Go to arg previous spelling error."
  (interactive "p")
  (while (not (= 0 arg))
    (let ((pos (point))
          (min (point-min)))
      (if (and (eq (current-buffer) flyspell-old-buffer-error)
               (eq pos flyspell-old-pos-error))
          (progn
            (if (= flyspell-old-pos-error min)
                ;; goto beginning of buffer
                (progn
                  (message "Restarting from end of buffer")
                  (goto-char (point-max)))
              (backward-word 1))
            (setq pos (point))))
      ;; seek the next error
      (while (and (> pos min)
                  (let ((ovs (overlays-at pos))
                        (r '()))
                    (while (and (not r) (consp ovs))
                      (if (flyspell-overlay-p (car ovs))
                          (setq r t)
                        (setq ovs (cdr ovs))))
                    (not r)))
        (backward-word 1)
        (setq pos (point)))
      ;; save the current location for next invocation
      (setq arg (1- arg))
      (setq flyspell-old-pos-error pos)
      (setq flyspell-old-buffer-error (current-buffer))
      (goto-char pos)
      (if (= pos min)
          (progn
            (message "No more miss-spelled word!")
            (setq arg 0))))))


(defun flyspell-next-and-ispell-word (args)
  ""
  (interactive "P")
	(progn
		(flyspell-goto-next-error)
		(ispell-word)))

(defun flyspell-previous-and-ispell-word (args)
  ""
  (interactive "P")
	(progn
		(flyspell-goto-previous-error)
		(ispell-word)))

(defun flyspell-goto-next-and-correct (args)
  ""
  (interactive "P")
	(progn
		(flyspell-goto-next-error)
		(flyspell-correct-word-generic)
		(right-char)))

(defun flyspell-goto-previous-and-correct (args)
  ""
  (interactive "P")
	(progn
		(flyspell-goto-previous-error 1)
		(flyspell-correct-word-generic)
		(left-word)))

Flyspell face:

(face-spec-set 'flyspell-incorrect '((t (:inherit error))))

flyspell-correct

https://github.com/d12frosted/flyspell-correct

Correcting misspelled words with flyspell using your favourite interface. Mine is helm.

(use-package flyspell-correct
	:ensure t
	:after flyspell
	:config
	(use-package flyspell-correct-helm
		:ensure t)
	;; (add-hook 'flyspell-mode-hook #'flyspell-correct-auto-mode)
	;; (setq flyspell-correct-auto-delay 0.8)
	:bind ("M-$" . flyspell-correct-at-point)
	)

guess-language

https://github.com/tmalsburg/guess-language.el

Minor mode for robust automatic language detection that automatically switches the spell checker accordingly.

This mode assumes that flyspell is activated and configured for all relevant languages, i.e., those listed in guess-language-languages.

(use-package guess-language
	:ensure t
  :pin MELPA
  :init (add-hook 'text-mode-hook #'guess-language-mode)
	:config
	(setq guess-language-languages '(en de))
	(setq guess-language-min-paragraph-length 35)
	:diminish guess-language-mode)

To see what dictionaries are installed, call

(mapcar 'car ispell-dictionary-alist)

spell-fu

https://codeberg.org/ideasman42/emacs-spell-fu

Fast spell checking without external programs.

jinx

  • State “TODO” from [2024-11-02 Sat 21:40]
  • [ ] Compilation throws an error

https://github.com/minad/jinx

An alternative to flyspell that uses Enchant. Only the visible part of the buffer is checked.

Requirements:

  • libenchant-2-dev (>v2.3.1)
  • pkgconf
(use-package jinx
	:ensure t
  ;; :hook (emacs-startup . global-jinx-mode)
  ;; :bind (("M-$" . jinx-correct)
  ;;        ("C-M-$" . jinx-languages))
)

Grammar checking

flycheck-vale

emacs-langtool

Grammar checking with LanguageTool. It requires to download resources from here: https://languagetool.org/download/

(use-package langtool
	:ensure t
	:config
	(setq langtool-language-tool-jar
				(expand-file-name (concat langtool-dir "/languagetool-commandline.jar"))))

Paren handling

smartparens

https://github.com/Fuco1/smartparens

Dealing with pairs of symbols (e.g. parens) in Emacs. Similar to embrace.

(use-package smartparens
	:ensure t
	:init
	(--each '(LaTeX-mode-hook
						R-mode-hook
						TeX-mode-hook
						bibtex-mode-hook
						css-mode-hook
						emacs-lisp-mode-hook
						java-mode-hook
						js-mode-hook
						markdown-mode-hook
						mu4e-compose-mode-hook
						org-mode-hook
						perl-mode-hook
						plantuml-mode-hook
						python-mode-hook
						sh-mode-hook
						shell-mode-hook
						xmg-mode-hook
						web-mode-hook)
		(add-hook it #'smartparens-mode))
	(require 'smartparens-config)					; loads configurations for several modes, e.g. latex-mode
	:config
	(setq sp-autoescape-string-quote nil)
	(setq sp-show-pair-from-inside nil)
	(setq smartparens-strict-mode t)			; skip over delimiters when deleting
	;; for all modes
	(sp-pair "(" nil :unless '(sp-point-before-word-p))
	(sp-pair "[" nil :unless '(sp-point-before-word-p))
	(sp-pair "{" nil :unless '(sp-point-before-word-p))
	(sp-pair "\"" nil :unless '(sp-point-before-word-p
															sp-point-after-word-p
															tl/sp-point-next-to-non-space-p))
	;; org-mode
	(sp-with-modes '(org-mode mu4e-compose-mode)
		(sp-local-pair "*" "*"
									 ;; :actions '(insert wrap) ; Don't know what this is good for.
									 :unless '(sp-point-before-word-p
														 sp-point-after-word-p
														 sp-point-at-bol-p
														 sp-in-math-p
														 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "_" "_" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 sp-in-math-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "/" "/" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 sp-in-math-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "~" "~" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 sp-in-math-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "=" "=" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 sp-in-math-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "+" "+" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 sp-in-math-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "$" "$" :unless '(sp-point-before-word-p
																		 sp-point-after-word-p
																		 tl/sp-point-next-to-non-space-p) )
		(sp-local-pair "«" "»"))
	;; latex-mode
	(sp-local-pair 'LaTeX-mode "$" nil :unless '(sp-point-before-word-p sp-point-after-word-p))
	)

(defun tl/sp-point-after-hash-p (id action context)
	"Return t if point is after a hash, nil otherwise.
	This predicate is only tested on \"insert\" action.
	Its definition follows the one of sp-point-after-word-p."
	(when (eq action 'insert)
		(sp--looking-back-p (concat "\\(#\\)" (regexp-quote id)))))

(defun tl/sp-point-next-to-non-space-p (id action context)
  "Return t if the character before or after point is a non-space
  character. This predicate is only tested on \"insert\" action. Its
  definition follows the one of sp-point-after-word-p."
  (and (eq action 'insert)
       (or (sp--looking-back-p "[^ \t\n]")
					 (sp--looking-at-p "[^ \t\n]"))))

;; jump to matching paren
(defun goto-match-paren (arg)
	"Go to the matching  if on (){}[], similar to vi style of % "
	(interactive "p")
	;; first, check for "outside of bracket" positions expected by forward-sexp, etc.
	(cond ((looking-at "[\[\(\{]") (forward-sexp))
				((looking-back "[\]\)\}]" 1) (backward-sexp))
				;; now, try to succeed from inside of a bracket
				((looking-at "[\]\)\}]") (forward-char) (backward-sexp))
				((looking-back "[\[\(\{]" 1) (backward-char) (forward-sexp))
				(t nil)))
(global-set-key (kbd "C-M-m") 'goto-match-paren)
(global-set-key (kbd "M-(") 'sp-backward-sexp)
(global-set-key (kbd "M-)") 'sp-forward-sexp)
(global-set-key (kbd "M-m") 'goto-match-paren)
(global-set-key (kbd "M-[") 'sp-beginning-of-sexp)
(global-set-key (kbd "M-]") 'sp-end-of-sexp)
(global-set-key (kbd "M-DEL") nil)
(global-set-key (kbd "M-DEL M-[") 'sp-unwrap-sexp)

emacs-pairs

;; https://ebzzry.github.io/emacs-pairs.html
;; (defmacro def-pairs (pairs)
;;   `(progn
;;      ,@(loop for (key . val) in pairs
;;           collect
;;             `(defun ,(read (concat
;;                             "wrap-with-"
;;                             (prin1-to-string key)
;;                             "s"))
;;                  (&optional arg)
;;                (interactive "p")
;;                (sp-wrap-with-pair ,val)))))
;; (def-pairs ((paren        . "(")
;;             (bracket      . "[")
;;             (brace        . "{")
;;             (single-quote . "'")
;;             (double-quote . "\"")
;;             (back-quote   . "`")))
;; (global-set-key (kbd "C-[") 'wrap-with-brackets) ; TODO: find nice key bindings
;; (global-set-key (kbd "C-(") 'wrap-with-parens)
;; (global-set-key (kbd "C-{") 'wrap-with-braces)

embrace

  • State “TODO” from [2018-09-03 Mo 23:13]

https://github.com/cute-jumper/embrace.el

Add/Change/Delete pairs based on expand-region, similar to evil-surround and smartparens.

Issues:

  • [ ] Still does not load correctly in org-mode.
(use-package embrace 
	:ensure t
	;; :hook (org-mode . embrace-org-mode-hook)
	:bind
	(("C-( a" . embrace-add)
	 ("C-( d" . embrace-delete)
	 ("C-( c" . embrace-change))
	:config
	(defun tl/embrace-delete-* ()
		(interactive)
		(embrace--delete ?*))
	(defun tl/embrace-delete-/ ()
		(interactive)
		(embrace--delete ?/))
	(defun tl/embrace-delete-_ ()
		(interactive)
		(embrace--delete ?_))
	(defun tl/embrace-delete-+ ()
		(interactive)
		(embrace--delete ?+))
	(defun tl/embrace-delete-= ()
		(interactive)
		(embrace--delete ?=))
	(defun tl/embrace-delete-~ ()
		(interactive)
		(embrace--delete ?~))

	(defun tl/embrace-org-mode-hook ()
		"Mostly taken from embrace-org-mode-hook which is defined in embrace.el."
		(dolist (lst '((?= "=" . "=")
									 (?~ "~" . "~")
									 (?/ "/" . "/")
									 (?* "*" . "*")
									 (?_ "_" . "_")
									 (?+ "+" . "+")
									 (?k "@@html:<kbd>@@" . "@@html:</kbd>@@")
									 (?l "@@latex:" . "@@")))
			(embrace-add-pair (car lst) (cadr lst) (cddr lst)))
		(embrace-add-pair-regexp ?b "#\\+BEGIN_.*" "#\\+END_.*" 'embrace-with-org-block
														 (embrace-build-help "#+BEGIN_*" "#+END") t))

	;; (add-hook 'LaTeX-mode-hook 'embrace-LaTeX-mode-hook)
	;; (add-hook 'org-mode-hook 'embrace-org-mode-hook)
	(add-hook 'org-mode-hook 'tl/embrace-org-mode-hook))

Indentation

Auto-indent when yanking https://www.emacswiki.org/emacs/AutoIndentation

(dolist (command '(yank yank-pop))
	(eval `(defadvice ,command (after indent-region activate)
					 (and (not current-prefix-arg)
								(member major-mode '(emacs-lisp-mode lisp-mode
																										 clojure-mode    scheme-mode
																										 haskell-mode    ruby-mode
																										 rspec-mode      python-mode
																										 c-mode          c++-mode
																										 objc-mode       latex-mode
																										 plain-tex-mode))
								(let ((mark-even-if-inactive transient-mark-mode))
									(indent-region (region-beginning) (region-end) nil))))))

Shift/delete indentation at cursor position or region:

;; shift-region is taken from http://stackoverflow.com/a/6918574/6452961
(defun shift-region (distance)
	"Shift the selected region right if distance is positive, left if negative"
  (let ((mark (mark)))
    (save-excursion
      (indent-rigidly (region-beginning) (region-end) distance)
      (push-mark mark t t)
      ;; Tell the command loop not to deactivate the mark
      ;; for transient mark mode
      (setq deactivate-mark nil))))

(defun tl/delete-indentation ()
  (interactive)
  (if (use-region-p)
			(shift-region -999)
		(indent-rigidly (line-beginning-position) (line-end-position) -999)
		))

aggressive-indent

https://github.com/Malabarba/aggressive-indent-mode

aggressive-indent-mode is a minor mode that keeps your code always indented. Not sure whether I want to auto-indent whole files …

(use-package aggressive-indent
  :ensure t
  :config
	;; (add-hook 'prog-mode-hook #'aggressive-indent-mode) ; Bad: Will also be active in Makefiles
	;; (dolist (hook '(emacs-lisp-mode-hook
	;; 								css-mode-hook
	;; 								clojure-mode))
	;; 	(add-hook hook #'aggressive-indent-mode))
  (global-aggressive-indent-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'html-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'sql-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'web-mode)
	(add-to-list 'aggressive-indent-excluded-modes 'makefile-mode)
	)

highlight-indent-guides

  • State “TODO” from [2025-01-10 Fri 08:30]

https://github.com/DarthFennec/highlight-indent-guides

Show vertical line per indentation level.

Issues:

  • [ ] Vertical lines sometimes do not have the right color (white or barely visible). This is independent of highlight-indent-guides-method.
(use-package highlight-indent-guides
	:ensure t
	:config
	(add-hook 'prog-mode-hook 'highlight-indent-guides-mode)
	(setq highlight-indent-guides-method 'bitmap)
	)

indent-bars

  • State “TODO” from [2024-09-26 Thu 14:45]

https://github.com/jdtsmith/indent-bars

Indentation guide supporting tree-sitter. Maybe an alternative to highlight-indent-guides?

Selection

expand-region

https://github.com/magnars/expand-region.el

Expand region increases the selected region by semantic units. I’ve bound it to C-+.

(use-package expand-region
	:ensure t
	:bind ("C-+" . er/expand-region)
	)

SOMEDAY expreg

https://github.com/casouri/expreg

Similar to expand-region, but uses tree-sitter.

Mouse selection

  • State “TODO” from [2021-02-11 Thu 09:22]

Copy mouse selection to kill-ring:

;; (setq mouse-drag-copy-region t)

Drag’n drop of text with the mouse:

(setq mouse-drag-and-drop-region t)
  • [ ] Does not work in org-mode

drag-stuff

https://github.com/rejeep/drag-stuff.el

Drag around marked stuff (words, region, lines) with the keyboard.

There is a irreconcilable conflict in the key bindings of drag-stuff and org-mode, see here. I have therefore added hydra-drag-stuff and will use it in hydra-transpose.

(use-package drag-stuff
	:ensure t
	:config
	(drag-stuff-global-mode 1)
	;; (drag-stuff-define-keys)							; Use <M-up>, <M-down>, <M-left> and <M-right>
	)

iedit

https://github.com/victorhge/iedit

Edit regions with identical content in the same way simultaneously. Cool!

(use-package iedit
	:ensure t)

Code folding

hideshow

(use-package hideshow
  :ensure t
  :diminish hs-minor-mode
  :config
	(add-hook 'prog-mode-hook 'hs-minor-mode))

hideshow-org adds org-mode like folding experience to hideshow.

(use-package hideshow-org
	:ensure t
  :config
	(add-hook 'prog-mode-hook 'hs-org/minor-mode))

Context menu

(global-set-key (kbd "<mouse-3>") 'mouse-major-mode-menu)
(global-set-key (kbd "<C-down-mouse-3>") 'mouse-popup-menubar)

Org-mode has its own context menu:

(require 'org-mouse)

lacarte

https://www.emacswiki.org/emacs/download/lacarte.el

Menu completion at the keyboard

(use-package lacarte
  :bind ("C-<f6>" . lacarte-execute-menu-command))

Cursor actions

General

This is supposed to accelerate cursor movement (https://emacs.stackexchange.com/a/28746/12336):

(setq auto-window-vscroll nil)

ace-jump

https://github.com/winterTTr/ace-jump-mode

Fasting moving of the cursor to any position in the buffer. Just type the first character of the word or line, and then type the shown jump code.

(use-package ace-jump-mode
	:ensure t
	:bind 
	("C-c SPC" . ace-jump-mode))

dump-jump

https://github.com/jacktasia/dumb-jump

Jump to definition without using tags. Requires external programs (ag, rg). This packages uses the xref interface of Emacs.

(use-package dumb-jump
  :ensure t
  :bind (("M-g o" . dumb-jump-go-other-window)
         ("M-g j" . dumb-jump-go)
         ("M-g x" . dumb-jump-go-prefer-external)
         ("M-g z" . dumb-jump-go-prefer-external-other-window))
  :config
	(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
	(setq dumb-jump-selector 'helm
				dumb-jump-max-find-time 10)
	)

multiple-cursors

https://github.com/magnars/multiple-cursors.el

Multiple cursors for Emacs. Super cool!

(use-package multiple-cursors
	:ensure t
	;; :bind
	;; ("C-S-c C-S-c" . mc/edit-lines)
	;; ("C->" . mc/mark-next-like-this)
	;; ("C-<" . mc/mark-previous-like-this)
	;; ("C-c C-<" . mc/mark-all-like-this)
	)

point-undo

Cursor position history (LOCAL)

(require 'point-undo)
(global-set-key [M-left] 'point-undo)
(global-set-key [M-right] 'point-redo)
(global-set-key (kbd "M-j") 'point-undo)
(global-set-key (kbd "M-k") 'point-redo)
;; focus on line-based jumps
;; http://emacs.stackexchange.com/a/28078/12336
(defvar point-undo-ring-length 100)
(defvar point-undo-ring (make-ring point-undo-ring-length))
(make-variable-buffer-local 'point-undo-ring)
(defvar point-redo-ring (make-ring point-undo-ring-length))
(make-variable-buffer-local 'point-redo-ring)
(defun point-undo-pre-command-hook ()
  "Save positions before command."
  (unless (or (eq this-command 'point-undo)
              (eq this-command 'point-redo))
    (let ((line (line-number-at-pos)))
      (when (eq line (cdr (nth 0 (ring-elements point-undo-ring))))
        (ring-remove point-undo-ring 0))
      (ring-insert point-undo-ring (cons (point) line))
      (setq point-redo-ring (make-ring point-undo-ring-length)))))
(add-hook 'pre-command-hook 'point-undo-pre-command-hook)
(defun point-undo-doit (ring1 ring2)
  "ring1, ring2 = {point-undo-ring, point-redo-ring}"
  (condition-case nil
      (progn
        (goto-char (car (nth 0 (ring-elements ring1)))) 
        (ring-insert ring2 (ring-remove ring1 0)))
    (error nil)))
(defun point-undo ()
  "Undo position."
  (interactive)
  (point-undo-doit point-undo-ring point-redo-ring))
(defun point-redo ()
  "Redo position."
  (interactive)
  (when (or (eq last-command 'point-undo)
            (eq last-command 'point-redo))
    (point-undo-doit point-redo-ring point-undo-ring)))

cursor-undo

https://elpa.gnu.org/packages/cursor-undo.html

goto-last-change

https://github.com/camdez/goto-last-change.el

Move cursor position back in undo history.

(use-package goto-last-change
	:ensure t
	:bind
	("M-_" . goto-last-change))

smarter C-a and C-e

Copied from http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/

(defun my/smarter-move-beginning-of-line (arg)
  "Move point back to indentation of beginning of line.

Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.

If ARG is not nil or 1, move forward ARG - 1 lines first.  If
point reaches the beginning or end of the buffer, stop there."
  (interactive "^p")
  (setq arg (or arg 1))

  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

;; remap C-a to `smarter-move-beginning-of-line'
(global-set-key [remap move-beginning-of-line]
                'my/smarter-move-beginning-of-line)

iregister

https://github.com/atykhonov/iregister.el

Helper functions to use the Emacs register (to save positions, text and more) interactively. They are primarily used in hydra-position-register.

(use-package iregister
	:ensure t)

Keystroke visualizer

https://github.com/chuntaro/emacs-keypression

Lorem ipsum / Blindtext

lorem-ipsum

https://github.com/jschaf/emacs-lorem-ipsum

Nam euismod tellus id erat.

(use-package lorem-ipsum
	:ensure t
	:config
	(global-set-key (kbd "C-c i l") 'lorem-ipsum-insert-paragraphs)
	)

Undo & remove

undo-tree

https://www.dr-qubit.org/undo-tree.html https://gitlab.com/tsc25/undo-tree

Visualize the undo history.

Issues:

  • [X] waiting for v0.8 to appear on elpa
    • In retrospect, I don’t remember the exact reason. Maybe a broken undo history in v0.7?
(use-package undo-tree
	:ensure t
  :diminish undo-tree-mode
  :config
  (global-undo-tree-mode)
  (setq undo-tree-visualizer-timestamps t
				undo-tree-visualizer-diff t)
	(global-unset-key (kbd "C-z"))
	(global-set-key (kbd "C-z")   'undo-tree-undo)
	(global-set-key (kbd "C-S-z") 'undo-tree-redo)
	:custom
	(undo-tree-auto-save-history nil)
	)

SOMEDAY vundo

https://github.com/casouri/vundo

Visual helper for the regular undo. See blog post: https://archive.casouri.cat/note/2021/visual-undo-tree/index.html Requires Emacs 28.

Remove newlines

http://stackoverflow.com/a/5194503

(defun remove-newlines-in-region ()
  "Removes all newlines in the region."
  (interactive)
  (save-restriction
    (narrow-to-region (point) (mark))
    (goto-char (point-min))
    (while (search-forward "\n" nil t) (replace-match " " nil t))))

(defun remove-newline ()
	(save-excursion 
		(search-forward "\n")
		(replace-match " ")))

(defun remove-newlines-or-blank-lines-dwim ()
	(interactive)
	(progn (if (use-region-p)
						 (remove-newlines-in-region)
					 (if (this-line-empty-p)
							 (delete-blank-lines)
						 (remove-newline)
					 ))))

(defun next-line-empty-p ()
  (save-excursion
		(next-line)
    (beginning-of-line)
    (looking-at "[[:space:]]*$")))

(defun this-line-empty-p ()
  (save-excursion
    (beginning-of-line)
    (looking-at "[[:space:]]*$")))

Remove extra spaces

Remove extra spaces from line or region (TODO):

(defun tl/remove-extra-spaces-dwim ()
	"Remove extra spaces in line or in region."
	(interactive)
	(if (region-active-p)
			(save-restriction
				(narrow-to-region (point) (mark))
				(save-excursion 
					(goto-char (point-min))
					(while (re-search-forward "[ ]+" nil t) (replace-match " " nil t))))
		(save-excursion
			(move-beginning-of-line nil)  					; FIXME: undo doesn't see save-excursion
			(while (re-search-forward "[ ]+" (line-end-position) t)
				(replace-match " "))))
	)

Tracking changes

hungry-delete

https://github.com/nflath/hungry-delete

Delete consecutive white space characters in one go.

(use-package hungry-delete
  :ensure t 
  ;; :config
  ;; (global-hungry-delete-mode)
	)

Save, kill & yank

SOMEDAY easy-kill

  • State “TODO” from [2018-11-09 Fri 19:00]

https://github.com/leoliu/easy-kill https://emacsredux.com/blog/2018/11/09/an-easy-kill/

Search & replace

visual-regexp

https://github.com/benma/visual-regexp.el

Like replace-regexp, but with live visual feedback directly in the buffer.

(use-package visual-regexp
	:ensure t
	:config
	(define-key global-map (kbd "C-c r") 'vr/replace)
	(define-key global-map (kbd "C-c q") 'vr/query-replace))

visual-regexp-steroids

https://github.com/benma/visual-regexp-steroids.el/

This is an extension to visual-regexp for using more standard (i.e. Python) regular expressions. There is also an interface to multiple-cursors.

(use-package visual-regexp-steroids
	:ensure t)

visual-replace

https://github.com/szermatt/visual-replace

Visual helper for standard query-replace in Emacs similar to visual-regexp, but with a prompt that lets one modify the search and the replacement at the same time.

I’m not using it right now.

Narrowing

Taken from http://endlessparentheses.com/emacs-narrow-or-widen-dwim.html:

(defun narrow-or-widen-dwim (p)
	"Widen if buffer is narrowed, narrow-dwim otherwise.
Dwim means: region, org-src-block, org-subtree, or
defun, whichever applies first. Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer
is already narrowed."
	(interactive "P")
	(declare (interactive-only))
	(cond ((and (buffer-narrowed-p) (not p)) (widen))
				((region-active-p)
				 (narrow-to-region (region-beginning)
													 (region-end)))
				((derived-mode-p 'org-mode)
				 ;; `org-edit-src-code' is not a real narrowing
				 ;; command. Remove this first conditional if
				 ;; you don't want it.
				 (cond ((ignore-errors (org-edit-src-code) t)
								(delete-other-windows))
							 ((ignore-errors (org-narrow-to-block) t))
							 (t (org-narrow-to-subtree))))
				((derived-mode-p 'latex-mode)
				 (LaTeX-narrow-to-environment))
				(t (narrow-to-defun))))

;; This line actually replaces Emacs' entire narrowing
;; keymap, that's how much I like this command. Only
;; copy it if that's what you want.
(define-key ctl-x-map "n" #'narrow-or-widen-dwim)
(add-hook 'LaTeX-mode-hook
          (lambda ()
            (define-key LaTeX-mode-map "\C-xn"
              nil)))

The keymap in org-mode seems to require a special treatment:

(add-hook 'org-mode-hook
          (lambda ()
            (define-key org-mode-map "\C-xn"
              nil)))

Taken from https://emacs.stackexchange.com/a/21356/12336:

(defun org-narrow-to-here ()
	"Exclude the current heading while narrowing."
  (interactive)
  (save-excursion
    (narrow-to-region
     (progn (unless (org-at-heading-p) (org-next-visible-heading -1))
            (forward-line)
            (point))
     (progn (org-next-visible-heading 1)
            (point)))))

Commenting

Keys

C-; is used to insert inline comments across major modes.

(global-set-key (kbd "C-;") 'comment-or-uncomment-region-or-line)
(eval-after-load "LaTeX-mode"
	'(define-key LaTeX-mode-map (kbd "C-;") 'comment-or-uncomment-region-or-line))
(eval-after-load "markdown-mode"
	'(define-key LaTeX-mode-map (kbd "C-;") 'comment-or-uncomment-region-or-line))
(defun comment-or-uncomment-region-or-line ()
  "Comments or uncomments the region or the current line if there's no active region."
  (interactive)
  (let (beg end)
    (if (region-active-p)
				(setq beg (region-beginning) end (region-end))
      (setq beg (line-beginning-position) end (line-end-position)))
    (comment-or-uncomment-region beg end)
    (next-line)))

Comment boxes

(defun insert-comment-box ()
  "Insert a comment box around the region."
  (interactive)
  (comment-box (region-beginning) (region-end)))

rebox2

poporg

https://github.com/QBobWatson/poporg

Poporg lets one edit comments in a separate buffer.

(use-package poporg
	:ensure t
	:bind (("C-c '" . poporg-dwim))
	:init
	(setq-default poporg-edit-hook 'org-mode)
	(add-hook 'TeX-mode-hook 
						#'(lambda ()
							 "poporg configuration"
							 (define-key TeX-mode-map (kbd "C-c '") 'poporg-dwim)
							 (setq poporg-edit-hook 'TeX-mode)))
	(add-hook 'LaTeX-mode-hook 
						#'(lambda ()
							 "poporg configuration"
							 (define-key LaTeX-mode-map (kbd "C-c '") 'poporg-dwim)
							 (setq poporg-edit-hook 'LaTeX-mode)))
	:config
	(define-key poporg-mode-map (kbd "C-c '") 'poporg-dwim)) 

Critical markup

https://github.com/joostkremers/criticmarkup-emacs

Support of CriticMarkup in Emacs.

(use-package cm-mode
	:ensure t
	:config
	(setq-default cm-author user-acronym))

Inline comments with links

http://kitchingroup.cheme.cmu.edu/blog/2015/04/24/Commenting-in-org-files/

Quoting

boxquote

  • State “TODO” from [2023-12-23 Sat 12:19]

https://github.com/davep/boxquote.el

Mark quotes with brackets on the left side.

,----[ Title ]
|  Example quote
`----

Issues

  • [ ] Create hydra for boxquotes
(use-package boxquote
	:ensure t
	:pin MELPA)

Additional DWIM function to use boxquote:

(defun boxquote-dwim ()
"Do what I mean when using boxquote."
	(interactive)
	(if (region-active-p)
			(if (boxquote-quoted-p)
					(call-interactively 'boxquote-unbox-region)
				(call-interactively 'boxquote-region))
		(if (boxquote-quoted-p)
				(call-interactively 'boxquote-unbox)
			(call-interactively 'boxquote-text))))

Line actions

Delete line

Some general keys (I barely use):

;; (global-set-key (kbd "C-d C-o") 'delete-blank-lines) ; not allowed here, see underi-mode.el
;; (global-set-key (kbd "C-d C-m") 'delete-blank-lines)
(global-set-key (kbd "C-S-k") 'kill-whole-line)
;; (global-set-key (kbd "C-k") 'kill-sentence) ; too greedy
(global-set-key (kbd "C-S-d") 'kill-whole-line)

Copy/cut whole line or region (I mostly use):

(global-set-key (kbd "C-w") 'xah-cut-line-or-region) ; cut
(global-set-key (kbd "M-w") 'xah-copy-line-or-region) ; copy

(defun xah-cut-line-or-region ()
  "Cut current line, or text selection.
When `universal-argument' is called first, cut whole buffer (respects `narrow-to-region').
URL `http://ergoemacs.org/emacs/emacs_copy_cut_current_line.html'
Version 2015-06-10"
  (interactive)
  (if current-prefix-arg
      (progn ; not using kill-region because we don't want to include previous kill
        (kill-new (buffer-string))
        (delete-region (point-min) (point-max)))
    (progn (if (use-region-p)
               (kill-region (region-beginning) (region-end) t)
             (kill-region (line-beginning-position) (line-beginning-position 2))))))

(defun xah-copy-line-or-region ()
  "Copy current line, or text selection.
When called repeatedly, append copy subsequent lines.
When `universal-argument' is called first, copy whole buffer (respects `narrow-to-region').
URL `http://ergoemacs.org/emacs/emacs_copy_cut_current_line.html'
Version 2016-06-18"
  (interactive)
  (let (-p1 -p2)
    (if current-prefix-arg
        (setq -p1 (point-min) -p2 (point-max))
      (if (use-region-p)
          (setq -p1 (region-beginning) -p2 (region-end))
        (setq -p1 (line-beginning-position) -p2 (line-end-position))))
    (if (eq last-command this-command)
        (progn
          (progn ; hack. exit if there's no more next line
            (end-of-line)
            (forward-char)
            (backward-char))
          ;; (push-mark (point) "NOMSG" "ACTIVATE")
          (kill-append "\n" nil)
          (kill-append (buffer-substring-no-properties (line-beginning-position) (line-end-position)) nil)
          (message "Line copy appended"))
      (progn
        (kill-ring-save -p1 -p2)
        (if current-prefix-arg
            (message "Buffer text copied")
          (message "Text copied"))))
		;; TL: commented out the following two lines. Cursor remains at region.
    ;; (end-of-line)   
    ;; (forward-char)
    ))

New line

(global-set-key (kbd "S-<return>") 'smart-open-line)
(global-set-key (kbd "C-x C-<return>") 'smart-open-line)
(global-set-key (kbd "C-S-<return>") 'smart-open-line-above)
(global-set-key (kbd "C-o") 'smart-open-line)
(global-set-key (kbd "C-S-o") 'smart-open-line-above)

;; http://emacsredux.com/blog/2013/03/26/smarter-open-line/
(defun smart-open-line ()
  "Insert an empty line after the current line.
Position the cursor at its beginning, according to the current mode."
  (interactive)
  (move-end-of-line nil)
  (newline-and-indent))

;; http://emacsredux.com/blog/2013/06/15/open-line-above/
(defun smart-open-line-above ()
  "Insert an empty line above the current line.
Position the cursor at it's beginning, according to the current mode."
  (interactive)
  (move-beginning-of-line nil)
  (newline-and-indent)
  (forward-line -1)
  (indent-according-to-mode))

Center line

This comes from vanilla Emacs:

(global-set-key (kbd "C-l") 'recenter-top-bottom)

Move line

move-text

https://github.com/emacsfodder/move-text

Move line or region up and down in a buffer using M-<up> and M-<down>.

(use-package move-text
	:ensure t
	:init
	(move-text-default-bindings)
	)

Capitalization

xah-toggle-letter-case

http://xahlee.info/emacs/emacs/modernization_upcase-word.html

Cycle through different kinds of capitalization. Requires titlecase.

(global-set-key (kbd "C-7") 'xah-toggle-letter-case)

(defun xah-toggle-letter-case ()
  "Toggle the letter case of current word or text selection.
Always cycle in this order: Init Caps, ALL CAPS, all lower.

URL `http://xahlee.info/emacs/emacs/modernization_upcase-word.html'
Version 2020-06-26"
  (interactive)
  (let (
        (deactivate-mark nil)
        $p1 $p2)
    (if (use-region-p)
        (setq $p1 (region-beginning) $p2 (region-end))
      (save-excursion
        (skip-chars-backward "[:alpha:]")
        (setq $p1 (point))
        (skip-chars-forward "[:alpha:]")
        (setq $p2 (point))))
    (when (not (eq last-command this-command))
      (put this-command 'state 0))
    (cond
     ((equal 0 (get this-command 'state))
      (upcase-initials-region $p1 $p2)
      (put this-command 'state 1))
     ((equal 1 (get this-command 'state))
      (upcase-region $p1 $p2)
      (put this-command 'state 2))
     ((equal 2 (get this-command 'state))
      (downcase-region $p1 $p2)
      (put this-command 'state 3))
		 ((equal 3 (get this-command 'state))
      (titlecase-dwim)
      (put this-command 'state 0)))))

Titlecase

https://github.com/duckwork/titlecase.el

Convert text to title case in Emacs

(use-package titlecase
	:ensure t)

Special characters

helm + org-entities-help

Insert special characters with helm based on org-entities-help. Copied from https://github.com/jkitchin/scimax/blob/master/scimax-org.el.

(defun helm-insert-org-entity ()
  "Helm interface to insert an entity from `org-entities'.
F1 inserts utf-8 character
F2 inserts entity code
F3 inserts LaTeX code (does not wrap in math-mode)
F4 inserts HTML code
F5 inserts the entity code."
  (interactive)
  (helm :sources
				(reverse
				 (let ((sources '())
							 toplevel
							 secondlevel)
					 (dolist (element (append
														 '("* User" "** User entities")
														 org-entities-user org-entities))
						 (when (and (stringp element)
												(s-starts-with? "* " element))
							 (setq toplevel element))
						 (when (and (stringp element)
												(s-starts-with? "** " element))
							 (setq secondlevel element)
							 (add-to-list
								'sources
								`((name . ,(concat
														toplevel
														(replace-regexp-in-string
														 "\\*\\*" " - " secondlevel)))
									(candidates . nil)
									(action . (("insert utf-8 char" . (lambda (x)
																											(mapc (lambda (candidate)
																															(insert (nth 6 candidate)))
																														(helm-marked-candidates))))
														 ("insert org entity" . (lambda (x)
																											(mapc (lambda (candidate)
																															(insert
																															 (concat "\\" (car candidate))))
																														(helm-marked-candidates))))
														 ("insert latex" . (lambda (x)
																								 (mapc (lambda (candidate)
																												 (insert (nth 1 candidate)))
																											 (helm-marked-candidates))))
														 ("insert html" . (lambda (x)
																								(mapc (lambda (candidate)
																												(insert (nth 3 candidate)))
																											(helm-marked-candidates))))
														 ("insert code" . (lambda (x)
																								(mapc (lambda (candidate)
																												(insert (format "%S" candidate)))
																											(helm-marked-candidates)))))))))
						 (when (and element (listp element))
							 (setf (cdr (assoc 'candidates (car sources)))
										 (append
											(cdr (assoc 'candidates (car sources)))
											(list (cons
														 (format "%10s %s" (nth 6 element) element)
														 element))))))
					 sources))))
(global-set-key (kbd "C-c i E") 'helm-insert-org-entity)

unicode-math-input

https://github.com/astoff/unicode-math-input.el

Insert mathematical symbols as Unicode characters.

(use-package unicode-math-input
	:ensure t
	:bind ("C-c i m" . unicode-math-input)
	:init
	(setq unicode-math-input-insert-tex t))

Speech to text

whisper.el

https://github.com/natrys/whisper.el

Speech-to-Text interface for Emacs using OpenAI’s whisper speech recognition model. This will download and compile whisper.ccp the first time whisper-run is executed. Note that whisper.el inserts text in bigger chunks, not in a word-by-word fashion: calling whisper-run the first time starts the recording; calling whisper-run a second time then initiates the transcription of the recording.

(use-package whisper
  :load-path "lisp/whisper.el"
  :bind ("C-c i w" . whisper-run)
  :config
  (setq whisper-install-directory share-dir
        whisper-model "base"
        whisper-language "auto"
        whisper-translate nil))

Thesauri

synosaurus

https://github.com/hpdeifel/synosaurus

Thesaurus frontend for Emacs with pluggable backends:

(use-package synosaurus
	:ensure t
	:config
	(setq-default synosaurus-choose-method "default"
								synosaurus-backend 'synosaurus-backend-openthesaurus)
	)

powerthesaurus

https://github.com/SavchenkoValeriy/emacs-powerthesaurus

Uses powerthesaurus.org for English.

(use-package powerthesaurus
	:ensure t)

Translation

dictcc

https://github.com/martenlienen/dictcc.el

Look up translations in the translation dictionaries of dict.cc.

(use-package dictcc
	:ensure t
	:bind
	(("C-0" . dictcc))
)

Call dictcc with word under cursor or marked region:

(defun tl/dictcc-at-point ()
  (interactive)
	(if (use-region-p)
			(dictcc (concat "\"" (filter-buffer-substring (region-beginning) (region-end)) "\""))
		(if (word-at-point)
				(dictcc (word-at-point))
      (call-interactively 'dictcc))
		))
(global-set-key (kbd "C-0") 'tl/dictcc-at-point)

go-translate

https://github.com/lorniu/go-translate

Translation framework for Emacs.

(use-package go-translate
:ensure t
:config
(setq gt-langs '("de" "en"))

;;;; Example from Github
;; (setq gt-default-translator
;;       (gt-translator
;;        :taker   (gt-taker :text 'buffer :pick 'paragraph)  ; config the Taker
;;        :engines (list (gt-bing-engine) (gt-google-engine)) ; specify the Engines
;;        :render  (gt-buffer-render)))                       ; config the Render

(setq gt-default-translator
      (gt-translator
       :taker   (gt-taker :text 'word :pick 'paragraph)  
       :engines (list 
								 ;; (gt-bing-engine)
      					 (gt-deepl-engine :key user-deepl-key)
								 (gt-google-engine)
								 ;; (gt-google-rpc-engine)
								 )
       :render (gt-buffer-render)))
)

DEPRECATED:

(use-package go-translate
:ensure t
:config
(setq gts-translate-list '(("de" "en")))

;; (setq gts-default-translator (gts-translator :engines (gts-bing-engine)))

(setq gts-default-translator
      (gts-translator
       :picker (gts-prompt-picker)
       :engines (list 
								 ;; (gts-bing-engine)
      					 (gts-deepl-engine :auth-key user-deepl-key :pro nil)
								 (gts-google-engine)
								 ;; (gts-google-rpc-engine)
								 )
       :render (gts-buffer-render)))
)

Printing

This solution reuses code from here:

(require 'ps-print)

;; In order to avoid printing from buffers with dark theme
;; (use-package load-theme-buffer-local
;; 	:ensure t)

(defun tl/harden-visual-line-breaks-in-buffer ()
	"Insert real newlines at visual line breaks."
	(interactive)
	(save-excursion
		(goto-char (point-min))
		(while (= 0 (save-excursion (forward-line)))
			(unless (this-line-empty-p)
				(end-of-visual-line)
				(newline)
				;; Kill empty line if it was just inserted
				(when (this-line-empty-p)
					(kill-line)))
			(forward-line))))

;; Print line numbers when in prog-mode
(add-hook 'prog-mode-hook
					(lambda () (setq ps-line-number t)))

(defun tl/print-buffer-to-pdf ()
	"Print the current buffer or region to print_buffertitle_timestamp.pdf.
This command depends on the shell command `ps2pdf'."
	(interactive)
	(let* ((buf (current-buffer))
				 (path (read-directory-name "Save PDF to: " (expand-file-name "~/Downloads")))
				 (filename (concat path
													 "print_"
													 (replace-regexp-in-string "\\ \\|\\[\\|\\]\\|\\*" "" (buffer-name buf))
													 "_"
													 (format-time-string "%Y-%m-%d")))
				 (region-start-pos (when (region-active-p) (region-beginning)))
				 (region-end-pos (when (region-active-p) (region-end))))
		(with-temp-buffer
			(insert-buffer buf)
			(visual-line-mode t)
			(tl/harden-visual-line-breaks-in-buffer)
			(let ((start-pos (or region-start-pos (point-min)))
						(end-pos (or region-end-pos (point-max))))
				(if (y-or-n-p "Print with faces? ")
						(progn
							;; (load-theme-buffer-local 'leuven tempbuf) ; FIXME: doesn't seem to take effect
							(ps-print-with-faces start-pos end-pos (concat filename ".ps")))
					(progn (ps-print-without-faces start-pos end-pos (concat filename ".ps"))))))
		(shell-command (concat "ps2pdf " filename ".ps " filename ".pdf" ))
		(delete-file (concat filename ".ps"))
		(message (concat "PDF saved to " filename ".pdf"))))

Integers

change-number-at-point

Increase or decrease number at point, and when inside an org-mode timestamp, do the appropriate thing. But do not go below 0! Inspired by andy/increment-number-at-point from https://gist.github.com/argherna/6392696.

(defun tl/change-number-at-point (step)
	(interactive)
	(save-excursion
		(skip-chars-backward "0123456789")
		(or (looking-at "[0123456789]+")
				(error "No number at point"))
		(replace-match
		 (let ((number (string-to-number (match-string 0))))
			 (number-to-string
				(if (> (+ number step) 0)
						(+ number step)
					0))))
		))

(defun tl/change-number-dwim (step)
	(interactive)
	(cond
	 
	 ((and (org-at-clock-log-p) (> step 0)) (let ((org-clock-adjust-closest t))
																						(call-interactively 'org-timestamp-up)))
	 ((and (org-at-clock-log-p) (< step 0)) (let ((org-clock-adjust-closest t))
																						(call-interactively 'org-timestamp-down)))
	 ;; with `(org-at-timestamp-p 'lax)', the function matches any part of the document looking like a timestamp
	 ((and (org-at-timestamp-p 'lax) (> step 0)) 
		(org-timestamp-up))
	 ((and (org-at-timestamp-p 'lax) (< step 0))
		(org-timestamp-down))
	 (t (tl/change-number-at-point step))
	 ))

(global-set-key (kbd "C-8") (lambda () (interactive) (tl/change-number-dwim -1)))
(global-set-key (kbd "C-9") (lambda () (interactive) (tl/change-number-dwim +1)))

Numbered examples

numbex

https://github.com/enricoflor/numbex

Zooming

  • State “TODO” from [2022-08-10 Wed 12:14]

text-scale-adjust

Built into Emacs: https://www.gnu.org/software/emacs/manual/html_node/emacs/Text-Scale.html

Issues:

  • [ ] Only changes the default font.

Files

org-agenda files

Should be loaded after Buffer settings in order to take effect.

Org-mode files are usually bundled within a single directory stored in the variable org-directory. But if there are links to org-mode files outside org-directory, they should be included to org-agenda-files, too.

Since there can be a vast amount of org-mode files in org-directory, especially when using org-roam, only org-files with org-agenda-file-tag as filetag are used as org-agenda-files.

(defcustom org-files nil
	"List of org-mode files, which is a super set of org-agenda-files")
(defun tl/update-org-agenda-files ()
	(interactive)
	(require 'org-attach) ; in order not get disturbed by org-attach links
  (setq org-files 
				(find-org-files-recursively (expand-file-name (concat org-directory "/")))) ; trailing slash required
	
	;; search for linked org-mode files within org-files and add them
	(dolist (file org-files)
		(setq org-files (append org-files (find-links-to-org-files file))))

	;; remove duplicates from org-files
	(setq org-files (delete-duplicates org-files :test #'string-equal))

	;; org-agenda-files are org-files with a certain filetag
	(setq org-agenda-files ())
	(if org-agenda-file-tag								; include all files if org-agenda-file-tag is nil
			(dolist (file org-files)
				(if (tl/org-agenda-file-tag-p file)
						(setq org-agenda-files (cons file org-agenda-files)))))
	
	(customize-save-variable 'org-agenda-files org-agenda-files)
	(customize-save-variable 'org-files org-files)
	(message "org-agenda-files and org-files updated")

	(org-id-update-id-locations)
	(message "org-id-files updated")	
	)

Search for org-mode files recursively:

;; Modified http://stackoverflow.com/a/26548971/6452961
(require 'dash)
(defun find-org-files-recursively (dir) "Find all org-files in DIR."
			 (let ((output ()))	 
				 (unless (file-directory-p dir) (error "Not a directory `%s'" dir))
				 (unless (equal (directory-files dir nil org-agenda-file-regexp t) nil)
					 (setq output 
								 (append output
												 (-filter 								; filter files with org-extension
													(lambda (x) (not (string-match "#" x))) ; does not contain #-symbol
													(file-expand-wildcards (concat dir "*.org*")))))) ; also include files with extension .org_archive 
				 (dolist (file (directory-files dir nil nil t))
					 (unless (member file '("." ".."))
						 (let ((file (concat dir file "/")))
							 (when (file-directory-p file)
								 (setq output (append output (find-org-files-recursively file)))))))
				 output)
			 )

	;; ;; The following code does not search the org directory recursively:
  ;; (setq org-agenda-files
	;; 			(append
	;; 			 (list org-directory)
	;; 			 (file-expand-wildcards (concat org-directory "/*/*.org"))))

Return list of linked org-mode files:

(defun find-links-to-org-files (file)
	"Return list of linked org-mode files. 

Inspired by: http://stackoverflow.com/questions/38061773/add-linked-org-files-to-org-agenda-files"
	(interactive)
	(let ((output ()))
		(save-current-buffer
			(find-file file)
			(org-element-map  
					(org-element-parse-buffer)
					'link
				(lambda (x)
					(let ((org-link-path (org-element-property :path x))
								(org-link-type (org-element-property :type x)))
						(when (and (equal org-link-type "file") ; only links to files
											 (string-match-p "\\(^[a-z]:\\)\\|\\(~/\\)" "~/") ; relative paths are bad
											 (equal "org" (file-name-extension org-link-path)) ;only org-mode files
											 (file-exists-p org-link-path) ; only existing files
											 )
							(princ (concat " " org-link-path))
							(add-to-list 'output (expand-file-name org-link-path))
							)))))
		(switch-to-buffer (current-buffer))		; return to first buffer
		output))

Only keep org-agenda-files with a certain filetag:

(defcustom org-agenda-file-tag "orgagendafile" 
	"This variabel saves the filetag that identifies files that are relevant for org-agenda.

When set to nil, all org-files are added to org-agenda-files.
When set to \"\", no org -files are added to org-agenda-files." )
(defun tl/org-agenda-file-tag-p (file)
	(let ((found nil)
				(oafiletag org-agenda-file-tag))
		(save-current-buffer
			(find-file file)
			(dolist (tag org-file-tags)
				(if (string-equal tag oafiletag)
						(setq found t))))
		(switch-to-buffer (current-buffer))
		found))

Update org-agenda-files once a day during start-up:

(defcustom org-agenda-files-last-update nil 
	"This variable saves the date of the last update of org-agenda-files.")
(when (not (string-equal org-agenda-files-last-update (format-time-string "%Y-%m-%d")))
	(tl/update-org-agenda-files)
	(customize-save-variable 'org-agenda-files-last-update (format-time-string "%Y-%m-%d"))
	(message "org-agenda-files-last-update updated"))

Set refile targets to move entries inside org-files:

(setq org-refile-targets '(
													 (nil :maxlevel . 2)             ; refile to headings in the current buffer
													 (org-files :maxlevel . 2) ; refile to any of these files
													 ))

Helper function to export org-agenda-files to one icalendar file:

(defun tl/org-agenda-export-icalendar-file (&optional target)
	(let ((org-icalendar-combined-agenda-file (or target org-icalendar-combined-agenda-file)))
		(org-icalendar-combine-agenda-files)
		;; Remove org-mode timestamps from date titles
		(with-current-buffer (find-file-noselect org-icalendar-combined-agenda-file)
			(goto-char (point-min))
			(while (re-search-forward "<.*?>\\(\\(-\\|–\\)?<.*?>\\)?" nil t)
				(replace-match ""))
			(save-buffer))
		))

Search & find

ripgrep

https://github.com/dajva/rg.el

Use ripgrep in Emacs.

(use-package rg
  :ensure t
	;; :config
	;; (rg-enable-default-bindings)
	;; (rg-enable-menu)											; Magit-like interface (not working in v1.8.1) 
	)

Helper function to call rg in a specific directory:

(defun tl/call-rg-in-directory (dir)
	"Call rg (from rg.el) in DIR."
	(let ((default-directory dir))
		(call-interactively 'rg)))

pdfgrep

pdfgrep enables one to grep through PDFs. URL: https://github.com/jeremy-compostella/pdfgrep

(use-package pdfgrep
	:ensure t
	:config
	(pdfgrep-mode))

Backup

Emacs copies the old contents of a file before actually saving it, called the “backup”.

https://www.emacswiki.org/emacs/BackupFiles http://pragmaticemacs.com/emacs/auto-save-and-backup-every-save/

(setq
 backup-by-copying t     ; don't clobber symlinks
 kept-new-versions 10    ; keep 10 latest versions
 kept-old-versions 0     ; don't bother with old versions
 delete-old-versions t   ; don't ask about deleting old versions
 version-control t       ; number backups
 vc-make-backup-files t) ; backup version controlled files

The target directory is to determined by the variable backup-directory-alist.

;; write backup files to own directory
(setq backup-directory-alist
      `(("." . ,backup-dir)))

Show difference between current file buffer and auto-save file (taken from https://www.emacswiki.org/emacs/AutoSave):

(defun tl/diff-auto-save-file ()
  "Get auto-save #file# difference with current buffer."
  (interactive)
  (diff (make-auto-save-file-name) (current-buffer) nil 'noasync))

Autosave

https://www.gnu.org/software/emacs/manual/html_node/emacs/Auto-Save.html https://www.emacswiki.org/emacs/AutoSave

Locking files

https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Locks.html https://www.emacswiki.org/emacs/LockFiles

By default, Emacs will store a symbolic link with prefix .# for every file that is currently being edited in order to “lock” them and prevent collisions.

However, within Cryptomator volumes under Linux, these symbolic links are not properly handled and ls will throw an “Input/output error” when trying to read them. I’m therefore turning off the creation of lock files for the time being:

(setq create-lockfiles nil)

Line encodings

  • State “TODO” from [2020-04-16 Do 23:20]
  • [ ] plug it into hydra-dired

Living in both OS worlds, different line endings may be encountered in files that need to be unified. Here is a function to do that for several files which also works in dired.

(defun xah-change-file-line-ending-style (@files @style)
  "Change current file or dired marked file's newline convention.

When called non-interactively, *style is one of 'unix 'dos 'mac or any of accepted emacs coding system. See `list-coding-systems'.

URL `http://ergoemacs.org/emacs/elisp_convert_line_ending.html'
Version 2016-10-16"
  (interactive
   (list
    (if (eq major-mode 'dired-mode )
        (dired-get-marked-files)
      (list (buffer-file-name)))
    (ido-completing-read "Line ending:" '("Linux/MacOSX/Unix" "MacOS9" "Windows") "PREDICATE" "REQUIRE-MATCH")))
  (let* (
         ($codingSystem
          (cond
           ((equal @style "Linux/MacOSX/Unix") 'unix)
           ((equal @style "MacOS9") 'mac)
           ((equal @style "Windows") 'dos)
           (t (error "code logic error 65327. Expect one of it." )))))
    (mapc
     (lambda (x) (xah-convert-file-coding-system x $codingSystem))
     @files)))

(defun xah-convert-file-coding-system (@fpath @coding-system)
  "Convert file's encoding.
 *fpath is full path to file.
 *coding-system is one of 'unix 'dos 'mac or any of accepted emacs coding system. See `list-coding-systems'.

If the file is already opened, it will be saved after this command.

URL `http://ergoemacs.org/emacs/elisp_convert_line_ending.html'
Version 2015-07-24"
  (let ($buffer
        ($bufferOpened-p (get-file-buffer @fpath)))
    (if $bufferOpened-p
        (with-current-buffer $bufferOpened-p
          (set-buffer-file-coding-system @coding-system)
          (save-buffer))
      (progn
        (setq $buffer (find-file @fpath))
        (set-buffer-file-coding-system @coding-system)
        (save-buffer)
        (kill-buffer $buffer)))))

Project management

projectile

https://projectile.mx/

Project management based on Git repositories.

(use-package projectile
  :ensure t
  :commands (projectile-find-file 
						 projectile-switch-project) 
  :diminish projectile-mode
  :config
	(setq projectile-remember-window-configs t
				projectile-enable-caching t			  ; speed up projectile by caching index
				projectile-indexing-method 'alien ; use external commands like find, git etc. Maybe not supported in Windows.
				projectile-require-project-root t ; only use projectile in git projects
				;; projectile-keymap-prefix (kbd "C-x p") ; change keymap prefix (obsolete sind v2.0.0)
				projectile-switch-project-action 'projectile-dired ; use dired when switching projects
				)
	(define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map)
  (projectile-global-mode)
	)

Use helm for completions.

(use-package helm-projectile
	:ensure t
	:bind (("C-x p o" . helm-projectile-find-file)
				 ("C-x p p" . helm-projectile-switch-project)
				 ("C-x p h" . helm-projectile)
				 ("C-x p g" . helm-projectile-grep)
				 ("C-x p s" . helm-projectile-grep))
	)

Windows and frames

General key bindings

Better keys for closing and switching between windows. The rest is done in hydra-f2.

	(global-set-key (kbd "M-s-<left>")  'windmove-left)
	(global-set-key (kbd "M-s-<right>") 'windmove-right)
	(global-set-key (kbd "M-s-<up>")   'windmove-up)
	(global-set-key (kbd "M-s-<down>")  'windmove-down)

	;; close window
	(global-set-key (kbd "M-<f2>") 'delete-window)

winner-mode allows you to undo (and redo) changes in the window configuration.

(winner-mode 1)

Navigation

Goto other window:

(define-key dired-mode-map (kbd "M-s") nil)
(global-set-key (kbd "M-s") 'other-window)

transpose-frame

https://www.emacswiki.org/emacs/TransposeFrame

Interactive functions which allow users to transpose windows arrangement in currently selected frame.

(use-package transpose-frame
  :ensure t
  ;; :bind ("H-t" . transpose-frame)
)

dimmer

  • State “TODO” from [2024-07-29 Mon 10:47]

https://github.com/gonewest818/dimmer.el

Visually highlight the selected buffer.

Issues

(use-package dimmer
	:ensure t
	:pin MELPA
	:config
	(dimmer-configure-which-key)					; which-key popups are not dimmed
	(dimmer-configure-helm) 							; helm buffers are not dimmed
	(dimmer-configure-hydra)							; hydra buffers are not dimmed
	(dimmer-configure-magit)							; magit trancient buffers are not dimmed
  ;; (dimmer-configure-org)								; org buffers are not dimmed
	(setq dimmer-adjustment-mode ':foreground) ; possible values are `:foreground', `:background', `:both'
	(setq dimmer-fraction 0.3) ; between 0 and 1; larger means more dimmed
	(dimmer-mode t))

Burly

https://github.com/alphapapa/burly.el

From the project decription: This package provides tools to save and restore frame and window configurations in Emacs, including buffers that may not be live anymore.

(use-package burly
	:ensure t
	:pin MELPA)

emacs-everywhere

https://github.com/tecosaur/emacs-everywhere https://emacstil.com/til/2021/10/04/edit-text-everywhere-with-emacs/

Open and edit everything in an Emacs frame.

(use-package emacs-everywhere
	:ensure t)

One can evoke emacs-everywhere from everywhere with:

emacsclient --eval "(emacs-everywhere)"

I have bound this to Super-e. Under Linux, make sure that the dependencies are satisfied:

sudo apt install xclip xdotool

Bookmarks

Bookmarks are built into Emacs and similar to registers: they store positions one can jump back to. “Unlike registers, they have long names, and they persist automatically from one Emacs session to the next.”

General settings

(setq bookmark-default-file my-bookmarks-file) ; default bookmark file
(setq bookmark-save-flag 1) ; everytime bookmark is changed, automatically save it
(global-set-key (kbd "C-x C-r") 'helm-bookmarks)

Git

magit

https://github.com/magit/magit

A Git porcelain inside Emacs.

(use-package magit
	:ensure t
	:pin MELPA
	:bind
	("C-x g" . magit-status)
	;; ("C-x C-g" . magit-status)
	:init
	;; make sure the latest version of magit-section is installed
	(use-package magit-section
		:ensure t
		:pin MELPA)
	:config
	;; show magit full screen
	(setq magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1)
	)

Forge

https://github.com/magit/forge

Work with Git forges from the comfort of Magit.

  • use extra functions of Git forges: issues, pull requests
  • very similar to magithub
  • supports Github and Gitlab out of the box. Further Git forges can be installed with ghub.

Usage:

Issues:

(use-package forge
	:ensure t
	:pin MELPA
	:after magit)

git-gutter

https://github.com/emacsorphanage/git-gutter

An Emacs port of the Sublime Text plugin GitGutter.

(use-package git-gutter
	:ensure t
	:config
	(global-git-gutter-mode +1)
	;; (git-gutter:linum-setup) ; git-gutter is struggling with linum-mode (DEPRECATED)
	(custom-set-variables
	 '(git-gutter:update-interval 2))
	:bind
	("C-x C-g" . nil)
	("C-x C-g TAB" . git-gutter:popup-hunk)
	("C-x C-g _" . git-gutter:revert-hunk)
	("C-x C-g z" . git-gutter:revert-hunk)
	("C-x C-g C-g" . git-gutter-mode)
	("C-x C-g g" . git-gutter-mode)
	("C-x C-g n" . git-gutter:next-hunk)
	("C-x C-g p" . git-gutter:previous-hunk)
	)

git-link

https://github.com/sshaw/git-link

Create URLs for files and commits in GitHub/Bitbucket/GitLab/… repositories.

(use-package git-link
	:ensure t
	:bind (("C-c g l" . git-link))
	)

Kill ring

(defun xah-show-kill-ring ()
  "Insert all `kill-ring' content in a new buffer.

URL `http://ergoemacs.org/emacs/emacs_show_kill_ring.html'
Version 2017-06-19"
  (interactive)
  (let (($buf (generate-new-buffer "untitled")))
    (progn
      (switch-to-buffer $buf)
      (funcall 'fundamental-mode)
      (setq buffer-offer-save t)
      (dolist (x kill-ring )
        (insert x "\n--------------------------------------------------\n\n"))
      (goto-char (point-min)))))

Shells

better-shell

https://github.com/killdash9/better-shell

Better-shell simplifies shell management and sudo access.

(use-package better-shell
    :ensure t
    ;; :bind (("C-'" . better-shell-shell)
    ;;        ("C-;" . better-shell-remote-open))
		)

eshell

https://www.gnu.org/software/emacs/manual/html_mono/eshell.html

Some tweaks of the built-in Eshell.

(add-hook 'eshell-mode-hook
					#'(lambda () (define-key eshell-mode-map (kbd "<tab>") 'completion-at-point)))
(setq eshell-cmpl-dir-ignore "\`\\(CVS\\)/\\'") ; in order to complete `..` to `../`
(defun tl/eshell ()										; http://emacs.stackexchange.com/a/28603/12336
  (interactive)
  (eshell t))
(global-set-key (kbd "<f7> e") 'tl/eshell)

External terminal

Function to open external terminal at point. See discussion here.

(defun open-external-terminal-here ()
  (interactive "@")
	(let ((terminal-path (file-name-directory
												(if (string-equal (buffer-mode) "dired-mode")
														(dired-current-directory)
													(or load-file-name
															buffer-file-name
															""))))
				(terminal-command-prefix (first (open-external-terminal-here--terminal-command)))
				(terminal-command-postfix (second (open-external-terminal-here--terminal-command))))
		(shell-command (concat terminal-command-prefix
													 terminal-path
													 terminal-command-postfix) nil nil)))
(defun open-external-terminal-here--desktop-environment ()
	"Return the desktop environment as string.
Taken from: https://emacs.stackexchange.com/a/52585/12336"
  (interactive)
  (let (
				;; Create new variable with DE name
				(de_env (getenv "XDG_CURRENT_DESKTOP"))
				;; Make sure search is case insensitiveopen-external-terminal-here
				(case-fold-search t))
    (cond ((eq system-type 'darwin)
					 "darwin")
					((memq system-type '(windows-nt ms-dos cygwin))
					 "windows")
					((string-match ".*kde.*" de_env)
					 "kde")
					((string-match ".*gnome.*" de_env)
					 "gnome")
					((string-match ".*unity.*" de_env)
					 "unity")
					((string-match ".*xfce.*" de_env)
					 "xfce")
					((string-match ".*lxde.*" de_env)
					 "lxde")
					((string-match ".*mate.*" de_env)
					 "mate")
					((string-match ".*cinnamon.*" de_env)
					 "cinnamon")
					(t "unknown"))))
(defun open-external-terminal-here--terminal-command ()
	"Return the prefix and postfix of the terminal command depending on the desktop environment as pair of strings."
  (alist-get (open-external-terminal-here--desktop-environment)
         '(("darwin" . ("open -a Terminal.app" ""))
           ("windows" . ("cmd.exe /C start cmd.exe" ""))
           ("kde" . ("konsole --workdir " " > /dev/null 2>&1 & disown"))
           ("gnome" . ("gnome-terminal --working-directory=" " > /dev/null 2>&1 & disown"))
           ("xfce" . ("xfce4-terminal --working-directory=" " > /dev/null 2>&1 & disown")))
					 '("x-terminal-emulator --working-directory=" " > /dev/null 2>&1 & disown") nil 'equal))

vterm

https://github.com/akermu/emacs-libvterm

A native linux shell inside Emacs.

(use-package vterm
  :ensure t
	:config
	(add-hook 'vterm-mode-hook (lambda () (setq-local winkeys-mode nil)))
	(setq vterm-buffer-name-string "vterm %s") ; set to a string including %s for multiple vterms
	:bind
	(:map vterm-mode-map
         ("M-s" . nil)))

During installation, vterm-module is compiled, which depends on

  • cmake
  • libtool
  • libtool-bin

Note that, in order to use all the features of vterm, also the underlying linux shell needs to be configured. See https://github.com/akermu/emacs-libvterm#shell-side-configuration-files for more information.

echo '# vterm settings
if [[ "$INSIDE_EMACS" = 'vterm' ]] \
    && [[ -n ${EMACS_VTERM_PATH} ]] \
    && [[ -f ${EMACS_VTERM_PATH}/etc/emacs-vterm-bash.sh ]]; then
	source ${EMACS_VTERM_PATH}/etc/emacs-vterm-bash.sh
fi' >> ~/.bashrc  

Data security

GnuPG / EasyPG

https://www.masteringemacs.org/article/keeping-secrets-in-emacs-gnupg-auth-sources

EasyPG Assistant (epa) is the Emacs assistant for interacting with GnuPG (i.e. OpenPGP, not S/MIME) that provides the following features:

  • Key management.
  • Cryptographic operations on regions.
  • Cryptographic operations on files.
  • Dired integration.
  • Mail-mode integration. (Not recommended!)
  • Automatic encryption/decryption of *.gpg files.

GnuPG keyrings can be used for encrypting a file by adding

-*- epa-file-encrypt-to: ("your@email.address") -*-

to the top.

Caveat: EasyPG (epg) itself does also handle S/MIME!

Variables:

(setq epg-user-id "123456789"						 ; GnuPG ID of your default identity.
			)

authinfo

Authinfo is basically a list of (hopefully encrypted) files that store user names and passwords.

Documentation

Look for authinfo file in authinfo-directory:

(setq auth-sources
    (list (expand-file-name ".authinfo.gpg" authinfo-directory)))

Function to look up password:

;; Taken from https://github.com/jwiegley/dot-emacs/blob/master/init.el
(defun lookup-password (host user port)
  (require 'auth-source)
  (funcall (plist-get (car (auth-source-search :host host :user user
                                               :type 'netrc :port port))
                      :secret)))

System management

proced

The built-in top-like process viewer in Emacs.

See https://www.masteringemacs.org/article/displaying-interacting-processes-proced or http://emacsredux.com/blog/2013/05/02/manage-processes-with-proced/

(require 'proced)
(setq proced-auto-update-interval 1)

(defun proced-settings ()
  (call-interactively 'proced-toggle-auto-update))
(add-hook 'proced-mode-hook 'proced-settings)

(global-set-key (kbd "C-x P") 'proced)

helm-system-packages

https://github.com/emacs-helm/helm-system-packages

A Helm interface to the package manager of your operating system.

(use-package helm-system-packages
	:ensure t
	:pin MELPA)

Guix

https://github.com/alezost/guix.el

Emacs front-end for the Guix package manager.

(use-package guix
	:ensure t
	:pin MELPA)

guix requires geiser-guile which oddly enough is not automatically installed as a dependency:

(use-package geiser-guile 
	:ensure t)

Also make sure that evironment variables are properly set (maybe restart required):

export GUIX_PROFILE=$HOME/.guix-profile
source $GUIX_PROFILE/etc/profile

Tags

etags

https://www.gnu.org/software/emacs/manual/html_node/emacs/Tags-Tables.html

etags is part of Emacs.

Web browsing

atomic-chrome

https://github.com/alpha22jp/atomic-chrome

Edit text areas of the browser in Emacs. A similar tool ist emacs-everywhere.

(use-package atomic-chrome
	:ensure t
	:config (atomic-chrome-start-server))

On the side of the browser, you need to install an extension such as GhostText.

xah-lookup

  • State “TODO” from [2020-04-01 Mi 10:51]
  • [ ] change keybindings
  • [ ] add German Wikipedia

http://ergoemacs.org/emacs/xah-lookup.html http://xahlee.info/emacs/emacs/xah-lookup.html

Look up information about words and phrases in online databases such as Wikpedia, Google Search etc.

(use-package xah-lookup
	:load-path "lisp/xah-lookup.el")

Remote connections

tramp

https://www.gnu.org/software/tramp/

Built-in package for editing remote files.

(require 'tramp)
(if (eq system-type 'windows-nt) 
    (progn
			(setq ange-ftp-ftp-program-name "ftp.exe")
			(setq tramp-default-method "plink"))
  (setq tramp-default-method "ssh"))
;; (setq tramp-default-user "")

There is a strange interaction with org-roam: Archive files (odt, zip, …) in my org-roam directory are mounted by Tramp when starting Emacs. Disabling tramp-archive.el as suggested in https://emacs.stackexchange.com/a/56345/12336 does not help.

(with-eval-after-load 'tramp-archive
  (setq tramp-archive-enabled nil)
	;; Remove Libreoffice file extensions
	(dolist (ext '("odb" "odf" "odg" "odp" "ods" "odt"))
		(setq tramp-archive-suffixes (delete ext tramp-archive-suffixes))))
;; Another try ...
(setq tramp-ignored-file-name-regexp "\\(^.\\.odt.\\)")

Last straw: disable Tramp alltogether. Only works when disabling Tramp already in init.el!

(with-eval-after-load 'tramp
	(setq tramp-mode nil))

emacs-ssh-deploy

Deploy local files and directories to remote hosts.

https://github.com/cjohansson/emacs-ssh-deploy

Help

helpful

https://github.com/Wilfred/helpful

A better help buffer.

(use-package helpful
	:ensure t
  :bind
  (("C-h f" . helpful-callable)
   ("C-h v" . helpful-variable)
   ("C-h k" . helpful-key)
   ("C-c C-d" . helpful-at-point)
   ("C-h F" . helpful-function)
   ("C-h C" . helpful-command)))

discover

https://github.com/mickeynp/discover.el

Discover more of emacs with the help of context menus. Similar to Hydras.

Currently I’m not using it because it binds M-s, which I use for switching between windows.

(use-package discover
	:ensure t)

Hydras

https://github.com/abo-abo/hydra

Hydras are contextual keybindings that are displayed in the minibuffer. Both the keybindings and the minibuffer display can be flexibly set.

Nice examples:

Comparison of Transient and Hydra (by the author of Transient): https://magit.vc/manual/transient/Comparison-With-Other-Packages.html#Hydra

hydra: begin

Lets start by activating the hydra package.

	(use-package hydra
		:ensure t
		:config

Hydras for function keys

The function keys are assigned to general hydras.

hydra-example.png

hydra-f1

Hydra in case you need help/documentation.

See also https://www.reddit.com/r/emacs/comments/3ba645/does_anybody_have_any_real_cool_hydras_to_share/cspqaly/

(defhydra hydra-f1 (:exit t :hint nil)

	"
    ╭────┐      
    │<f1>│  _<f2>_     _<f3>_     _<f4>_    _<f5>_      _<f6>_   _<f7>_   _<f8>_   _<f9>_ 
    │Help│  Window^^   Config^^   Check^^   Refresh^^   Mode^^   Open^^   Mark^^   Org^^
   ╭╯    └──^^─────────^^─────────^^────────^^──────────^^───────^^───────^^───────^^────╯

    Describe      ^^Keys                     ^^Elisp          ^^Documentation     ^^Processes
    --------------^^-------------------------^^---------------^^------------------^^-----------
    _m_ode          single _k_eybinding        _a_propros       _i_nfo              _S_ystem 
    _p_ackage       available _b_indings       _f_unction       _d_ocsets           _E_macs
    _c_ursor        _w_here is key             _v_ariable       _n_: man
    ^^              _y_asnippets               _L_ibrary

  "
	;; Boring help commands...
	("e" view-echo-area-messages "messages")
	("l" view-lossage "lossage")
	("C" describe-coding-system "coding system")
	("I" describe-input-method "input method")

	;; Documentation
	("i" info nil)
	("n" helm-man-woman nil)
	("d" helm-dash nil)

	;; Keybinds
	("b" describe-bindings nil)
	("c" describe-key-briefly nil)
	("k" describe-key nil)
	("w" where-is nil)
	("y" yas-describe-tables nil)

	;; Elisp
	("a" apropos-command nil)
	("s" info-lookup-symbol nil)
	("v" describe-variable nil)
	("f" describe-function nil)
	;; ("S" describe-syntax nil)
	("L" finder-commentary nil)

	;; Describe
	("p" describe-package nil)
	("m" describe-mode nil)
	("c" (what-cursor-position t) nil)

	;; ^Processes
	("S" proced nil)
	("E" list-processes nil)

	("<f2>" hydra-f2/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f8>" hydra-f8/body :exit t)
	("<f9>" hydra-f9/body :exit t)
	;; quit
	("q" help-quit "quit"))

(global-set-key (kbd "<f1>") #'hydra-f1/body)

hydra-f2

Hydra for everything about windows and frames.

(require 'hydra-examples)								; provides hydra-move-splitter-left etc.
(defhydra hydra-f2
	(:hint nil
				 :idle 0.2)
	"
         ^^  ╭──────┐       
     _<f1>_  │<f2>  │  _<f3>_     _<f4>_    _<f5>_      _<f6>_   _<f7>_   _<f8>_   _<f9>_ 
     Help^^  │Window│  Config^^   Check^^   Refresh^^   Mode^^   Open^^   Mark^^   Org^^
   ╭─────^^──╯      └────────^^────────^^──────────^^───^^───────^^───────^^───────^^────╯

          ^^  _<up>_            │→ _M-<right>_      [_z_] window undo            [_t_] transpose frame
          ^^   ↑   ^^           │split [_h_]oriz.   [_Z_] window redo            [_H_] swap horizontally
    _<left>_ ←   → _<right>_    │← _M-<left>_       [_f_] clone frame            [_V_] swap vertically
          ^^   ↓   ^^          ────────^^────────   [_b_] clone buffer         [_C-L_] scroll
          ^^ _<down>_           ↑ _M-<up>_          [_p_] print buffer to PDF  
          ^^ ^^                 split [_v_]ert.     [_=_] balance windows         [_q_]uit    
          ^^ ^^                 ↓ _M-<down>_
	"
	("<down>" windmove-down :exit t)
	("<up>" windmove-up :exit t)
	("<right>" windmove-right :exit t)
	("<left>" windmove-left :exit t) 
	("f" make-frame :exit t)
	("b" clone-indirect-buffer :exit t)
	("v" split-window-vertically :exit t)
	("h" split-window-horizontally :exit t)
	("M-<right>" hydra-move-splitter-right :exit nil)
	("M-<left>" hydra-move-splitter-left :exit nil)
	("M-<up>" hydra-move-splitter-up :exit nil)
	("M-<down>" hydra-move-splitter-down :exit nil)
	("=" balance-windows :exit t)
	("t" transpose-frame :exit t)
	("H" flop-frame :exit t)
	("C-L" hydra-scroll :exit t)
	("V" flip-frame :exit t)
	("z" winner-undo)
	("Z" winner-redo)
	("p" tl/print-buffer-to-pdf :exit t)
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f8>" hydra-f8/body :exit t)
	("<f9>" hydra-f9/body :exit t))
	(global-set-key (kbd "<f2>") 'hydra-f2/body)
(defhydra hydra-scroll (:hint nil)
	"Scroll buffer"
	("n" (lambda nil (interactive) (scroll-up 3)) :exit nil)
	("p" (lambda nil (interactive) (scroll-down 3)) :exit nil)
	("<down>" (lambda nil (interactive) (scroll-up 3)) "scroll down" :exit nil)
	("<up>" (lambda nil (interactive) (scroll-down 3)) "scroll up" :exit nil)
	("q" nil :color blue))
(global-set-key (kbd "C-S-l") 'hydra-scroll/body)

hydra-f3

Hydra for everything about configurating Emacs.

(defhydra hydra-f3
	(:hint nil
				 :idle 0.2)
	"

         ^^   ^^         ╭──────┐       
     _<f1>_    _<f2>_    │<f3>  │  _<f4>_    _<f5>_      _<f6>_   _<f7>_   _<f8>_   _<f9>_ 
     Help^^    Window^^  │Config│  Check^^   Refresh^^   Mode^^   Open^^   Mark^^   Org^^
   ╭─────^^─────────^^───╯      └───────^^──────────^^───^^───────^^───────^^───────^^────╯
 
         [_i_] init file             [_p_] package manager         [_m_] macros
         [_d_] .emacs.d              [_u_] upgrade packages        [_fp_] proportional font
         [_c_] customize emacs       [_t_] load theme              [_s_] system packages
        [_lv_] visual line mode     [_ln_] line numbers            [_P_] private settings
        [_lt_] truncate lines       [_lr_] relative line numbers                                [_q_]uit
	"
	("i" (find-file (expand-file-name org-init-file user-emacs-directory)) :exit t)
	("d" (find-file user-emacs-directory) :exit t)
	("fp" toggle-proportional :exit t)
	("c" customize :exit t)
	("p" paradox-list-packages :exit t)
	("P" (find-file private-emacs-settings-dir) :exit t)
	("s" helm-system-packages :exit t)
	("u" paradox-upgrade-packages :exit t)
	("m" hydra-macro/body :exit t)
	("t" hydra-load-theme/body :exit t)
	("ln" tl/choose-line-numbering :exit t)
	("lr" tl/toggle-relative-line-numbering :exit t)
	("lv" visual-line-mode :exit t)
	("lt" toggle-truncate-lines :exit t)
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f2>" hydra-f2/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f8>" hydra-f8/body :exit t)
	("<f9>" hydra-f9/body :exit t))
	(global-set-key (kbd "<f3>") 'hydra-f3/body)
(defun tl/choose-line-numbering ()
	(interactive)
	(if (version<= "26.0.50" emacs-version )
			(display-line-numbers-mode 'toggle)
		(linum-mode)))
(defun tl/toggle-relative-line-numbering ()
	(interactive)
	(if (not (eq display-line-numbers 'relative))
			(setq display-line-numbers 'relative)
		(setq display-line-numbers t)))

hydra-f4

Hydra for everything about checking buffer contents.

(defhydra hydra-f4
	(:hint nil
				 :idle 0.2)
	"
         ^^         ^^         ^^  ╭─────┐       
     _<f1>_   _<f2>_     _<f3>_    │<f4> │  _<f5>_      _<f6>_   _<f7>_   _<f8>_   _<f9>_ 
     Help^^   Window^^   Config^^  │Check│  Refresh^^   Mode^^   Open^^   Mark^^   Org^^
   ╭─────^^─────────^^─────────^^──╯     └─────────^^───^^───────^^───────^^───────^^─────╯
 
      [_e_] compilation errors      [_c_] code checking     [_s_] spell checking 
     [_#m_] count matches           [_r_] recover file      [_=_] inline calculator       
     [_#w_] count words/lines/chars [_t_] translate         [_L_] lookup in online resource    [_q_]uit
      [_p_] PDF signature  
 	"
	("e" hydra-compilation-error/body :exit t)
	("c" hydra-flycheck/body :exit t)
	("p" check-pdf-signature-dwim :exit t)
	("r" recover-this-file :exit t)
	("s" hydra-flyspell/body :exit t)
  ("t" gt-do-translate :exit t)
	("=" literate-calc-minor-mode :exit t)
	("L" hydra-lookup/body :exit t)
	("#w" count-words :exit t)
	("#m" count-matches :exit t)
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f2>" hydra-f2/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f8>" hydra-f8/body :exit t)
	("<f9>" hydra-f9/body :exit t))
(global-set-key (kbd "<f4>") 'hydra-f4/body)

hydra-lookup

(defhydra hydra-lookup (:hint nil)
	"Lookup in online resources"
	("d" tl/dictcc-at-point "translation dictionary")
	("p" powerthesaurus-hydra/body "powerthesaurus" :exit t)
	("g" xah-lookup-google "google" :exit t)
  ("t" gts-do-translate :exit t)
	("w" xah-lookup-wikipedia "wikipedia" :exit t)
	("q" nil :exit t))

hydra-f6

Hydra for functions specific to the buffer major mode.

(global-set-key (kbd "<f6>") 'hydra-f6/body)
(global-set-key (kbd "C-S-c") 'hydra-f6/body)

(defun hydra-f6/body () ;hydra-for-major-mode ()
	(interactive)
  (cond
	 ((string-equal (buffer-mode) "deft-mode")
		(hydra-deft/body))
	 ((string-equal (buffer-mode) "dired-mode")
		(hydra-dired-main/body))
	 ((string-equal (buffer-mode) "elfeed-search-mode")
		(mz/make-and-run-elfeed-hydra))
	 ((string-equal (buffer-mode) "image-mode")
		(hydra-image/body))
	 ((string-equal (buffer-mode) "latex-mode")
		(hydra-latex-main/body))
	 ((string-equal (buffer-mode) "bibtex-mode")
		(hydra-bibtex/body))
	 ;; Must appear before entry for org-mode
	 ((string-equal (car (org-babel-get-src-block-info)) "jupyter-python")
		(jupyter-org-hydra/body))
	 ((string-equal (buffer-mode) "org-mode")
		(hydra-org-main/body))
	 ((string-equal (buffer-mode) "org-agenda-mode")
		(hydra-org-agenda/body))
	 ((string-equal (buffer-mode) "pomidor-mode")
		(hydra-pomidor/body))
	 ((or (string-equal (buffer-mode) "markdown-mode")
				(string-equal (buffer-mode) "gfm-mode"))
		(hydra-markdown/body))
	 ((string-equal (buffer-mode) "smerge-mode")
		(unpackaged/smerge-hydra/body))
	 ((string-equal (buffer-mode) "emacs-lisp-mode")
		(hydra-elisp/body))
	 ((string-equal (buffer-mode) "pdf-view-mode")
		(hydra-pdftools/body))
	 ((string-equal (buffer-mode) "mu4e-headers-mode")
		(hydra-mu4e-headers/body))
	 ((string-equal (buffer-mode) "mu4e-view-mode")
		(hydra-mu4e-view/body))
	 ((string-equal (buffer-mode) "mu4e-compose-mode")
		(hydra-mu4e-compose/body))
	 ((string-equal (buffer-mode) "mastodon-mode")
		(hydra-mastodon/body))
	 ((string-equal (buffer-mode) "picture-mode")
		(hydra-artist/body))
	 ((or t) (message (concat "no hydra defined for major mode"))))
	)

Function to look up the major mode of a buffer:

(defun buffer-mode (&optional buffer-or-name)
  "Returns the major mode associated with a buffer.
If buffer-or-name is nil return current buffer's mode."
  (buffer-local-value 'major-mode
											(if buffer-or-name (get-buffer buffer-or-name) (current-buffer))))

For using a hydra within helm:

(define-key helm-map (kbd "<f6>") 'hydra-helm/body)

hydra-f7

Hydra for opening a new buffer.

(defhydra hydra-f7
	(:hint nil
				 :idle 0.2)
	"
         ^^         ^^         ^^       ^^        ^^      ^^      ╭────┐       
     _<f1>_   _<f2>_     _<f3>_     _<f4>_    _<f5>_      _<f6>_  │<f7>│  _<f8>_  _<f9>_ 
     Help^^   Window^^   Config^^   Check^^   Refresh^^   Mode^^  │Open│  Mark^^  Org^^
   ╭─────^^─────────^^─────────^^────────^^──────────^^───────^^──╯    └──────^^────────╯
 
       [_a_] address book       [_d_] dired/file browser     [_t_] pomodoro timer      [_m_] mail
       [_b_] new buffer         [_e_] emacs shell            [_l_] dictionary          [_M_] mastodon
       [_i_] buffer overview    [_s_] system shell           [_B_] bookmarks           [_v_] vterm 
       [_c_] calendar           [_S_] external shell        [_gl_] magit buffer log 
       [_p_] projectile         [_f_] search files          [_hb_] bibliography     
       [_P_] pandoc             [_r_] elfeed                 [_j_] Jupyter notebook
       [_o_] open externally    [_R_] R repl                [_gs_] magit status         [_q_]uit
       [_G_] guix
	" 
	("a" helm-khard :exit t)
	;; ("a" hydra-khardel/body :exit t)
	;; ("a" helm-bbdb :exit t)
	("c" (lambda ()
				 (interactive)
				 (if (get-buffer "*cfw-calendar*")
						 (switch-to-buffer "*cfw-calendar*")
					 (my-open-calfw))) :exit t)
	("d" dired-jump :exit t)
	("f" hydra-search/body :exit t)
	("s" shell :exit t)
	("S" open-external-terminal-here :exit t)
	("b" xah-new-empty-buffer :exit t)
	("hb" helm-bibtex :exit t)
	("i" ibuffer :exit t)
	("j" tl/open-jupyter-notebook :exit t)
	("l" dictcc :exit t)
	("m" mu4e :exit t)
	("M" mastodon :exit t)
	("G" guix :exit t)
	("gs" magit-status :exit t)
	("gl" magit-log-buffer-file :exit t)
	("e" tl/eshell :exit t)
	("B" bookmark-bmenu-list :exit t)
	("o" xah-open-in-external-app :exit t)
	("p" helm-projectile :exit t)
	("P" pandoc-main-hydra/body :exit t)
	;; ("r" tl/elfeed-summary :exit t)
	("r" tl/elfeed :exit t)
	("R" R :exit t)
	("t" pomidor :exit t)
	("v" vterm :exit t)
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f2>" hydra-f2/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f8>" hydra-f8/body :exit t)
	("<f9>" hydra-f9/body :exit t))
(global-set-key (kbd "<f7>") 'hydra-f7/body)
(defun tl/open-jupyter-notebook ()
	"Open iPython/Jupyter notebook within emacs.
This presupposes a running Jupyter server on localhost."
	(interactive)
	(call-interactively 'ein:notebooklist-login)
	(ein:notebooklist-open))

hydra-f8

Hydra for marking contents of a buffer.

(defhydra hydra-f8
	(:hint nil
				 :idle 0.2)
	"
         ^^         ^^         ^^       ^^        ^^      ^^       ^^      ╭─────┐       
     _<f1>_   _<f2>_     _<f3>_     _<f4>_    _<f5>_      _<f6>_   _<f7>_  │<f8> │  _<f9>_ 
     Help^^   Window^^   Config^^   Check^^   Refresh^^   Mode^^   Open^^  │Mark │  Org^^
   ╭─────^^─────────^^─────────^^────────^^──────────^^───────^^───────^^──╯     └─────^^─╯

       Mark & Edit                ^^Highlight                      ^^Bookmark
    ------------------------------^^-------------------------------^^-----------------------
      [_a_] mark all               [_c_] highlight changes          [_b_] add bookmark
      [_r_] mark rectangular       [_s_] highlight same symbols     [_p_] position register
      [_e_] edit same symbol      [_gg_] git gutter                [_gl_] git link                     
      [_*_] critic markup          [_w_] show/hide whitespaces                                         
      [_(_] key macro              [_o_] highlight with overlays  
      [_>_] box-quote              ^^                               [_q_]uit
	"
	("a" mark-whole-buffer :exit t)
	("gg" hydra-git-gutter/body :exit t)
	("gl" git-link :exit t)
	("c" hydra-highlight-changes/body :exit t)
	("r" rectangle-mark-mode :exit t)
	("b" hydra-add-bookmark/body :exit t)
	("e" iedit-mode :exit t)
	("o" ov-highlight/body :exit t)
  ("p" hydra-position-register/body :exit t)
	("s" hydra-highlight-symbol/body :exit t)
	;; ("t" hydra-tags/body :exit t)
	("w" whitespace-mode :exit t)
	("(" hydra-macro/body :exit t)
	(">" boxquote-dwim :exit t)
	("m" hydra-macro/body :exit t)
  ("*" hydra-cm-mode/body :exit t )
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f2>" hydra-f2/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f9>" hydra-f9/body :exit t)

	)
	(global-set-key (kbd "<f8>") 'hydra-f8/body)

hydra-f9

Hydra for global org-mode keys:

(defhydra hydra-f9
	(:hint nil
				 :idle 0.2)
	"
         ^^         ^^         ^^       ^^        ^^      ^^       ^^       ^^      ╭────┐       
     _<f1>_   _<f2>_     _<f3>_     _<f4>_    _<f5>_      _<f6>_   _<f7>_   _<f8>_  │<f9>│ 
     Help^^   Window^^   Config^^   Check^^   Refresh^^   Mode^^   Open^^   Mark^^  │Org │
   ╭─────^^─────────^^─────────^^────────^^──────────^^───────^^───────^^───────^^──╯    ╵
 
     ^^  [_a_] agenda  ^^    [_j_] show journal   ^^  ^^   ^^  ^^  ^^  [_r_] reload org       	
     ^^  [_d_] deft    ^^    [_t_] show todos   ^^  ^^   ^^  ^^  ^^    [_u_] update agenda files
     ^^  [_c_] capture ^^    [_:_] filter by tags ^^  ^^  ^^  ^^  ^^   [_C_] clocking...       
     ^^  [_f_] search  ^^    [_e_] export agenda files                 [_n_] show notes      
     ^^   ^^           ^^    ^^  ^^ ^^ ^^ ^^ ^^                                    [_q_]uit

	"
	("a" org-agenda-list-complete :exit t)
	("d" deft :exit t)
	("e" (lambda nil (interactive)
				 (tl/org-agenda-export-icalendar-file)) :exit t)
	("j" (lambda nil (interactive)
				 (find-file (concat org-directory "/journal.org"))) :exit t)
	("n" hydra-org-roam/body :exit t)
	("o" deft :exit t)
	("c" org-capture :exit t)
	("C" hydra-org-clock/body :exit t)
	("t" org-todo-list :exit t)
	("w" (lambda nil (interactive)
				 (find-file (concat org-directory "/work/work.org"))) :exit t)
	("h" (lambda nil (interactive)
				 (find-file (concat org-directory "/home/home.org"))) :exit t)
	(":" org-tags-view :exit t)
	("r" org-reload :exit t)
	;; ("f" helm-org-rifle-agenda-files :exit t) 
	("f" hydra-org-search/body :exit t)
	;; ("f" org-search-view :exit t)
	("s" org-search-view :exit t)
	("u" tl/update-org-agenda-files :exit t)
	("q" nil :color blue)
	("<f1>" hydra-f1/body :exit t)
	("<f2>" hydra-f2/body :exit t)
	("<f3>" hydra-f3/body :exit t)
	("<f4>" hydra-f4/body :exit t)
	("<f5>" revert-buffer :exit t)
	("<f6>" hydra-f6/body :exit t)
	("<f7>" hydra-f7/body :exit t)
	("<f8>" hydra-f8/body :exit t)

	)
(global-set-key (kbd "<f9>") 'hydra-f9/body)

hydra-artist

Hydra for artist-mode.

(defhydra hydra-artist (:color teal :hint nil)
	"
    ^^              ^^   ^^                            ^^                             ╭────────┐
    ^Draw^          ^^  ^Modify^                      ^Operations^                    │ artist │
	╭─^^──────────────^^───^^────────────────────────────^^─────────────────────────────┴────────╯
    _l_ line       _<_--_>_ add|remove arrow head    _E_ erase                  Prefix: C-c C-a  
    ^^              ^^  _v_ delete whole line      _RET_ start|end drawing 
    _p_ poly line            ^ ^                     
    _r_ rectangle ┐ ^^  ^ ^                        _M-w_ copy with rectangular
    _s_ square    │ ^^  _f_ fill                   _C-k_ cut with rectangular
    _c_ circle    │ ^^_C-f_ choose fill            _C-y_ paste
    _e_ ellipsis  ╯          ^ ^                    ^ ^                          
    _S_ spray can            ^ ^                    ^ ^
    _x_ pen line    ^^       ^ ^                   _C-o_ choose operation              _q_ Quit
"
	("l" artist-select-op-line)
	("<" artist-toggle-first-arrow)
	(">" artist-toggle-second-arrow)
	("p" artist-select-op-poly-line)
	("r" artist-select-op-rectangle)
	("M-w" artist-select-op-copy-rectangle)
	("C-k" artist-select-op-cut-rectangle)
	("C-y" artist-select-op-paste)
	("E" artist-select-op-erase-rectangle)
	("s" artist-select-op-square)
	("c" artist-select-op-circle)
	("e" artist-select-op-ellipse)
	("f" artist-select-op-flood-fill)
	("C-f" artist-select-fill-char)
	("S" artist-select-op-spray-can)
	("v" artist-select-op-vaporize-line)
	("x" artist-select-op-pen-line)
	("RET" artist-key-set-point)
	("C-o" artist-select-operation)
	("q" nil)
	)

hydra-bibtex

(defhydra hydra-bibtex (:color teal :hint nil)
	"
    ^^                      ^^                           ^^                          ╭────────┐
    ^Field^                 ^Entry^                      ^File^                      │ bibtex │
	╭─^^──────────────────────^^───────────────────────────^^──────────────────────────┴────────╯
    _f_ New                 _+_ New...                   _s_ Sort entries
    _k_ Kill                _c_ Clean                    _h_ Open helm-bibtex
    _o_ Remove OPT/ALT      _C_ Clean & generate key     _O_ org-ref hydra...
    _t_ Add timestamp       _w_ Kill                     _F_ File actions...
    _y_ Yank                _x_ Goto crossref            ^ ^
    _?_ Help                _i_ Import from DOI          ^ ^
    ^ ^                     _u_ Update                   ^ ^                          _q_ Quit
"
	("o" bibtex-remove-OPT-or-ALT)
	("c" bibtex-clean-entry)
	("C" (bibtex-clean-entry '(4)))
	("f"  bibtex-make-field)
	("C-k"  bibtex-kill-field)
	("k"  bibtex-kill-field)
	("C-w"  bibtex-kill-entry)
	("w"  bibtex-kill-entry)
	("u"  bibtex-entry-update)
	("y"  bibtex-yank)
	("s"  bibtex-sort-buffer)
	("t"  tl/bibtex-add-timestamp)
	("x"  bibtex-search-crossref)
	;; ("+"  org-ref-bibtex-new-entry/body "new entry..." :exit t)
	;; ("+"  hydra-bibtex-langsci-new-entry/body "new entry..." :exit t)
	("+"  bibtex-entry)
	("i"  get-bibtex-from-doi)
	("h"  helm-bibtex)
	("F"  org-ref-bibtex-file/body)
	("O"  org-ref-bibtex-hydra/body)
	("?" bibtex-print-help-message)
	("q" nil)
	)

hydra-bookmarks

(defhydra hydra-add-bookmark (:hint nil)
	"Add bookmark"
	("b" bookmark-set "buffer" :exit t)
	("f" burly-bookmark-frames "frames" :exit t)
	("w" burly-bookmark-windows "windows" :exit t)
	("q" nil :exit t))

hydra-cm-mode

(defhydra hydra-cm-mode
	(:body-pre (when (not (string-equal cm-mode "t"))
							 (font-lock-mode -1)
							 (font-lock-mode 1)
							 (cm-mode 1))
						 :hint nil)
			"
     CriticMarkup
   ╭──────────────╯
      [_<up>_] previous      [_a_] addition         [_i_] accept/reject
    [_<down>_] next          [_d_] deletion         [_I_] accept/reject all
     ^^                      [_s_] substitution
     [_F_] follow changes    [_c_] comment          [_q_]uit													
		 ^^											 ^^							  			[_Q_]uit and deactivate cm-mode
"
			("<up>" #'cm-backward-change)
			("<down>" #'cm-forward-change)
			("a" #'cm-addition :color blue)
			("d" #'cm-deletion :color blue)
			("s" #'cm-substitution :color blue)
			("c"#'cm-comment :color blue)
			("i" #'cm-accept/reject-change-at-point)
			("I" #'cm-accept/reject-all-changes)
			("t" #'cm-set-author)
			("F" #'cm-follow-changes :color blue)
			("q" nil :color blue)
			("Q" (cm-mode -1) :color blue))
(global-set-key (kbd "C-*") 'hydra-cm-mode/body)

hydra-dired

(defhydra hydra-dired-main (:hint nil)
	"
 ^Navigation^            ^^            ^Mark^              ^Actions^             ^View^
-^----------^------------^^------------^----^--------------^-------^-------------^----^----------------------

             _<up>_      ^^             _m_: mark           _+_: new              _g_: refresh
  ^^            ʌ        ^^             _u_: unmark         _C_: copy to          _/_: filter
  ^^                     ^^             _U_: unmark all     _D_: delete           _s_: sort
  _<left>_  ..     view  _<right>_      _t_: toggle mark    _R_: move to          _(_: details
  ^^               enter _RET_          _*_: specific       _r_: rename           _)_: collapse
  ^^                     ^^             ^^                  _S_: symlink          _z_: size
  ^^            v        ^^             _w_: copy file name _P_: change property _ow_: open in other window
            _<down>_     ^^             _W_: copy path      _c_/_Z_: compress      _of_: open in other frame
  ^^                     ^^             ^^                  _=_: diff
   _._: home  _j_: jump  _f_: find        _$_: wdired         _am_: attach to mail    ^^
   ^^         ^^         ^^             ^^                    _af_: goto attached heading
   ^^         ^^         ^^             ^^                    _&_: clone file                 _q_: quit hydra 
-^----------^------------------------^----^---------------^-------^-------------^----^---------------------
"
	("<left>" (lambda () (interactive) (find-alternate-file "..")))
	("<up>" diredp-previous-line)
	("<down>" diredp-next-line)
	("RET" dired-find-file)
	("<right>" dired-view-file)
	("+" hydra-dired-new/body :color blue)
	("*" hydra-dired-mark/body :color blue)
	("P" hydra-dired-properties/body :color blue)
	("am" gnus-dired-attach :color blue)
	("af" my-org-attach-visit-headline-from-dired :color blue)
	("O" dired-do-chown)
	("M" dired-do-chmod)
	("G" dired-do-chgrp)
	("C" dired-do-copy)
	("D" dired-do-delete)
	("<delete>" dired-do-delete)
	("R" dired-do-rename)
	("S" dired-do-symlink)
	("Z" dired-do-compress)
	("c" dired-do-compress-to)
	("/" dired-narrow)
	("(" dired-hide-details-mode)
	(")" dired-collapse-mode)
	("$" wdired-change-to-wdired-mode :color blue)
	("&" tl/dired-duplicate-this-file :color blue)
	("f" helm-find)
	("." (find-file home-directory))
	("=" hydra-dired-diff/body :color blue)  
	;; ("=" dired-diff)
	("g" revert-buffer)
	("j" dired-goto-file)
	("ow" dired-find-file-other-window :color blue)
	("of" diredp-find-file-other-frame :color blue)
	("r" dired-efap)
	;; ("s" dired-sort-toggle-or-edit)
	("s" hydra-dired-quick-sort/lambda-S :color blue)
	("m" dired-mark)
	("u" dired-unmark)
	("U" dired-unmark-all-marks)
	("t" dired-toggle-marks)
	("v" dired-view-file)
	("w" dired-copy-filename-as-kill)
	("W" tl/dired-copy-path-at-point)
	("z" dired-get-size)
	;; ("q" quit-window :color blue)
	("q" nil :color blue)
	)

(defhydra hydra-dired-new (:hint nil )
	"New"
	("d" dired-create-directory "directory"  :exit t :after-exit (hydra-dired-main/body))
	("f" find-file "file" :exit t)
	("s" dired-do-symlink "symbolic link" :exit t :after-exit (hydra-dired-main/body))
	("h" dired-do-hardlink "hard link" :exit t :after-exit (hydra-dired-main/body))
	("q" hydra-dired-main/body "quit" :color blue)
	)

(defhydra hydra-dired-mark (:hint nil :after-exit (hydra-dired-main/body))
	"Mark"
	("a" (dired-mark-files-regexp ".*") "all" :exit t)
	("d" dired-mark-directories "directories" :exit t)
	("." diredp-mark/unmark-extension "by extension" :exit t)
	("s" dired-mark-symlinks "symbolic links" :exit t)
	("r" dired-mark-files-regexp "by regexp" :exit t)
	("/" dired-mark-sexp "by search term" :exit t)
	("q" hydra-dired-main/body "quit" :exit t)
	)

(defhydra hydra-dired-properties (:hint nil :after-exit (hydra-dired-main/body))
	"Change"
	("o" dired-do-chown "owner")
	("r" dired-do-chmod "rights")
	("g" dired-do-chgrp "group")
	("q" hydra-dired-main/body "quit" :color blue)
	)

(defhydra hydra-dired-diff (:hint nil :after-exit (hydra-dired-main/body))
	"Diff"
	("=" diredp-ediff "ediff")
	("e" ora-ediff-files "ediff on marked")
	("d" ztree-diff "diff on directories")
	("p" diffpdf-dired--command "diffpdf")
	("q" hydra-dired-main/body "quit" :color blue)
	)

Don’t ask for the path when searching for files:

(defun my-find-name-dired (pattern)
  "My version of find-name-dired that always starts in my chosen folder"
  (interactive "sFind Name (file name wildcard): ")
  (dired-hide-details-mode)
  (find-name-dired "." pattern))

hydra-elisp

(defhydra hydra-elisp (:hint nil :color blue :columns 3)
	"Elisp mode"
	("df" edebug-defun "debug function" :color blue)
	("da" edebug-all-defs "debug all" :color blue)
	("dd" checkdoc "debug documentation" :color blue)
	("C-M-x" eval-defun nil :color blue)
	("ef" eval-defun "evaluate function" :color blue)
	("eb" eval-buffer "evaluate buffer" :color blue)
	("q" nil "quit" :color blue)
	)

hydra-org

(defhydra hydra-org-main (:hint nil :color blue)
	"
     ^^                           ^^                          ^^                       ^^               ╭──────────┐   
     ^Heading^                    ^Text^                      ^Filter^                  ^Navigation^    │ org-mode │
	╭──^^───────────────────────────^^──────────────────────────^^────────────────────────^^──────────────┴──────────╯ 
     _a_ attachment...            _l_ preview LaTeX           _C_ columns...       _<down>_ next visible heading
     _A_ archive...               _$_ preview LaTeX           _/_ filter             _<up>_ previous visible heading
     _b_ open in new buffer       _m_ markup...           _<tab>_ tidily unfold   _C-c C-u_ embedding heading
     _c_ clocking...              _r_ org-ref            _<left>_ hide subtree   _C-<down>_ next heading same level
     _i_ add ID                   _._ timestamp         _<right>_ show subtree     _C-<up>_ previous heading same level
     _n_ notes...                 _*_ cycle list type  _C-<left>_ hide sublevels        _o_ open link/timestamp/...
     _p_ add property             _#_ table...        _C-<right>_ show sublevels        _O_ open link/timestamp/... in new frame
     _P_ add property-value pair  _v_ toggle images       ^<f9>:^ filter tags globally  _j_ jump to heading
   _s_/\^ sort                     \\ toggle overlays         ^^                        _B_ jump to named block
     _t_ set todo keyword        _dl_ delete link
     _:_ set tags                _dt_ delete aux files  ^^                      ^^      _q_ quit
   _+_/_-_ priority up/down         _e_ editmarks...
     _1_ move to top              _D_ download
     _N_ number headings
     _x_ add/open hand notes
"
	("a" org-attach)
	("A" hydra-org-archive/body)
	("b" org-tree-to-indirect-buffer)
	("B" org-babel-goto-named-src-block :color pink)
	("C" hydra-org-columns/body)
	("D" hydra-org-download/body)
	("c" hydra-org-clock/body)
	("E" my-sem-hydra-start)
	("e" my-sem-hydra-start)
	("i" org-id-get-create)
	("j" org-goto :color pink)
	("l" org-toggle-latex-fragment)
	("dl" tl/org-replace-link-with-description)
	("dt" delete-org-latex-aux-files)
	("$" org-fragtog-mode)
	("m" hydra-org-markup/body)
	("n" hydra-org-roam/body)
	("N" org-num-mode)
	("o" org-open-at-point)
	("O" tl/org-open-in-new-frame)
	("p" org-set-property)
	("P" org-set-property-and-value)
	("r" org-ref)
	("s" org-sort)
	("x" tl/org-add-or-open-xournal-attachment)
	("/" org-sparse-tree)
	("." tl/org-timestamp-dwim)
	("<tab>" org-show-current-heading-tidily)
	("<left>" tl/org-hide-subtree-or-up-dwim :color pink)
	("C-<left>" outline-hide-sublevels :color pink)
	("C-<right>" tl/org-show-sublevels :color pink)
	("<right>" org-cycle :color pink)
	("t" org-todo)
	(":" org-set-tags-command)
	("+" org-priority-up :color pink)
	("-" org-priority-down :color pink)
	("1" bjm/org-headline-to-top :color pink)
	("*" org-cycle-list-bullet :color pink)
	("#" hydra-org-table/body)
	("v" org-toggle-inline-images)
	("\\" tl/org-toggle-overlays)
	("^" org-sort)
	("C-n" org-next-visible-heading :color pink)
	("<down>" org-next-visible-heading :color pink)
	("C-p" org-previous-visible-heading :color pink)
	("<up>" org-previous-visible-heading :color pink)
	("C-N" org-forward-heading-same-level :color pink)
	("C-P" org-backward-heading-same-level :color pink)
	("C-<down>" org-forward-heading-same-level :color pink)
	("C-<up>" org-backward-heading-same-level :color pink)
	("C-f" org-forward-heading-same-level :color pink)
	("C-b" org-backward-heading-same-level :color pink)
	("C-c C-u" outline-up-heading :color pink)
	("q" nil :color blue)
	)
(defun tl/org-toggle-overlays ()
	"Toggle the use of overlays that are used with links, emphasis markers, special symbols in the buffer."
	(interactive)
	(org-toggle-pretty-entities)
	(org-toggle-link-display)
	(if org-hide-emphasis-markers
			(setq org-hide-emphasis-markers nil)
		(setq org-hide-emphasis-markers t)))

Taken from https://emacs.stackexchange.com/a/26840/12336 and modified:

(defun tl/org-folded-p ()
	"Returns non-nil if point is on a folded headline or plain item."
	(and (or (org-at-heading-p)
					 (org-at-item-p))
			 (invisible-p (point-at-eol))))
(defun tl/org-hide-subtree-or-up-dwim ()
	(interactive)
	(if (tl/org-folded-p)
			(outline-up-heading 1)
		(outline-hide-subtree))
	)
(defun tl/org-show-sublevels ()
	(interactive)
	(outline-hide-sublevels (+ (org-current-level) 1))
	(recenter-top-bottom)
	)

hydra-org-archive

(defhydra hydra-org-archive (:hint nil)
	"Org-mode archive"
	("t" org-toggle-archive-tag "set archive tag" :exit t)
	("a" org-archive-to-archive-sibling "archive below" :exit t)
	("A" org-archive-subtree-default "archive outside" :exit t)
	("<tab>" org-force-cycle-archived "expand archive" :exit t)
	("q" nil :color blue)
	)

hydra-org-columns

A hydra for org-columns.

(defhydra hydra-org-columns (:hint nil :color pink :columns 3 :body-pre (progn (beginning-of-line) (org-columns)))
	"Org-mode columns"
	(">" org-columns-widen "widen")
	("<" org-columns-narrow "narrow")
	("a" org-columns-edit-allowed "allowed values" :exit t)
	("c" org-columns-content "content" :exit t)
	("e" org-columns-edit-value "edit" :exit t)
	("n" org-columns-next-allowed-value "next allowed value")
	("p" org-columns-previous-allowed-value "previous allowed value")
	("r" org-columns-redo "refresh")
	("s" org-columns-edit-attributes "select attribute")
	("t" org-columns-todo "todo")
	("2" (org-show-children 2) "Show up to 2 sublevels")
	("M-S-<left>" org-columns-delete "delete")
	("M-S-<right>" org-columns-new "new")
	("M-<right>" org-columns-move-right "move right")
	("M-<left>" org-columns-move-left "move left")
	("<delete>" org-columns-delete)
	("v" org-columns-show-value "show value")
	("q" org-columns-quit :color blue)
	)

hydra-org-clock

Hydra for clocking activities in org-mode (taken from https://github.com/abo-abo/hydra/wiki/Org-clock-and-timers):

(bind-key "C-c c" 'hydra-org-clock/body)
(defhydra hydra-org-clock (:color blue :hint nil)
  "
^Clock:^ ^In/out^     ^Edit^   ^Summary^    | ^Timers:^ ^Run^           ^Insert
-^-^-----^-^----------^-^------^-^----------|--^-^------^-^-------------^------
(_?_)    _i_n         _e_dit   _g_oto entry | (_h_)     _b_egin         ti_m_e
 ^ ^     _c_ontinue   _Q_uit   _d_isplay    |  ^ ^      cou_n_tdown     i_t_em
 ^ ^     _o_ut        ^ ^      _r_eport     |  ^ ^      _p_ause toggle
 ^ ^    h_I_istory    ^ ^      ^ ^          |  ^ ^      _s_top

"
  ("i" org-clock-in)
  ("c" org-clock-in-last)
  ("o" org-clock-out)
	("I" org-mru-clock-in)
  
  ("e" org-clock-modify-effort-estimate)
  ("Q" org-clock-cancel)

  ("g" org-clock-goto)
  ("d" org-clock-display)
  ("r" org-clock-report)
  ("?" (org-info "Clocking commands"))

  ("b" org-timer-start)
  ("n" org-timer-set-timer)
  ("p" org-timer-pause-or-continue)
  ("s" org-timer-stop)

  ("m" org-timer)
  ("t" org-timer-item)
  ("h" (org-info "Timers")))

hydra-org-download

Download with org-download.

(defhydra hydra-org-download (:hint nil)
	"Org-mode download"
	("c" org-download-clipboard "clipboard" :exit t)
	("d" org-download-delete "delete" :exit t)
	("r" org-download-rename-at-point "rename " :exit t)
	("s" org-download-screenshot "screenshot" :exit t)
	("u" org-download-image "url" :exit t)
	("y" org-download-yank "yank" :exit t)
	("q" nil :color blue)
	)

hydra-org-agenda

(defhydra hydra-org-agenda (:hint nil :color red)
	"
    ^^                     ^^                 ^^                       ^^                        ╭────────────┐
     ^Calendar^             ^Heading^          ^Clocking^               ^Filter^                 │ org-agenda │
	╭─^^─────────────────────^^─────────────────^^───────────────────────^^────────────────────────┴────────────╯
    _b_ backward d/w/y     _+_ priority up    _I_ clock in					   _/_ by tag            _S_ync with CalDAV
    _._ today              _-_ priority down  _O_ clock out					   _<_ by category    
    _f_ forward d/w/y      _t_ set todo       _J_ jump to clocked-in	 \^ siblings        
    _j_ goto date          _:_ set tags       _R_ clocking report  	   _|_ remove all     
    _d_ day view           _>_ set date       ^^                       _l_ show done (log mode) 
    _w_ week view    _C-c C-d_ set deadline   ^^
   _vm_ month view   _C-c C-s_ set schedule   ^^                       _C_ complete agenda
   _vy_ year view        _C-k_ kill           ^^                       _H_ home agenda ^^         _r_efresh
    ^^       _M-S-<up>_ earlier hour       _M-S-<up>_ earlier time     _W_ work agenda ^^         _q_uit hydra
    ^^     _M-S-<down>_ later hour       _M-S-<down>_ later time       _E_ events      ^^        e_x_it agenda
    ^^    _M-S-<right>_ earlier day            ^^                       
    ^^     _M-S-<down>_ later day              ^^                      _m_ mark						
    _h_ show holidays		^^									   ^^		 							     _u_ unmark          
    _]_ show inactive timestamps ^^					   ^^	 								     _B_ action on marked
    _k_ capture with timestamp	 ^^				     ^^			 								^^                  
"																																			
	("+" org-agenda-priority-up)
	("-" org-agenda-priority-down)
	("d" org-agenda-day-view)
	("w" org-agenda-week-view)
	("vm" org-agenda-month-view)
	("vy" org-agenda-year-view)
	("h" org-agenda-holidays)
	("H" org-agenda-list-home)
	("W" org-agenda-list-work)
	("C" org-agenda-list-complete)
	("E" org-agenda-list-events)
	("I" org-agenda-clock-in)
	("O" org-agenda-clockreport-mode)
	("R" org-agenda-clock-in)
	("S" tl/org-export-agenda-to-caldav :color blue)
	("f" org-agenda-later)
	("b" org-agenda-earlier)
	("F" org-agenda-follow-mode)
	("j" org-agenda-goto-date)
	("J" org-agenda-clock-goto)
	("." org-agenda-goto-today)
	("C-k" org-agenda-kill)
	("C-c C-s" org-agenda-schedule)
	("C-c C-d" org-agenda-deadline)
	("M-S-<left>" org-agenda-date-earlier)
	("M-S-<right>" org-agenda-date-later)
	("M-S-<up>" tl/org-agenda-alt-shift-up)
	("M-S-<down>" tl/org-agenda-alt-shift-down)
	("m" org-agenda-bulk-mark)
	("u" org-agenda-bulk-unmark)
	("B" org-agenda-bulk-action)
	("k" org-agenda-capture)
	("t" org-agenda-todo)
	("/" org-agenda-filter-by-tag)
	("|" org-agenda-filter-remove-all)
	("^" org-agenda-filter-by-top-headline)
	("l" org-agenda-log-mode)
	("]" org-agenda-manipulate-query-subtract)
	(":" org-agenda-set-tags)
	(">" org-agenda-date-prompt)
	("<" org-agenda-filter-by-category)
	("r" org-agenda-redo)
	("x" org-agenda-exit :color blue)
	("q" nil :color blue)
	)

hydra-org-search

(defhydra hydra-org-search (:color blue :hint nil)
	"Org-mode search"
	("a"
	 (lambda nil (interactive) 
		 (helm-org-rifle-files org-agenda-files)) 
	 "agenda files")
	("e"
	 ;; (let ((org-super-agenda-date-format "(W%V) %d %B %Y, %A"))
	 (org-ql-search
		 (org-agenda-files)
		 '(tags "event" "appointment")
		 :sort '(date)
		 ;; :super-groups '((:auto-ts reverse))
		 )
	 ;; )
	 "events")
	("f" (lambda nil (interactive)
				 (helm-org-ql org-files) ; FIXME: way too slow!
				 )
	 "free search")	
	("g" (org-ql-search
				 (org-agenda-files)
				 '(and
					 ;; Heading is DONE or an event not in a (potentially ongoing) project
					 (and (or (todo "DONE")
										;; Event must be in the past
										(and
										 ;; Tag hierarchies are not yet supported:
										 ;; https://github.com/alphapapa/org-ql/issues/145
										 (tags "event" "appointment")
										 (not (ts-active :from -60)))
										)
								(not (ancestors (tags "project"))))
					 ;; Heading has no recent clocks
					 (not (clocked :from -60))
					 ;; Descendents have no recent clocks
					 (not (descendants (clocked :from -60)))))
	 "garbage collection")
	("q" nil :color blue)
	)

hydra-org-table

Taken from https://gist.github.com/dfeich/1df4e174d45f05fb5798ca514d28c68a and modified:

(defhydra hydra-org-table (:color red :hint nil)
  "
         ^^                   ^^                            ^^                       ^^             ╭───────────┐   
         ^Cell^               ^Table^                    ^Formula^                 ^Navigation^     │ org-table │
	╭──────^^───────────────────^^──────────────────────────^^────────────────────────^^──────────────┴───────────╯ 

         _'_ edit cell        _s_ sort lines              _e_ eval formula          _<up>_ above cell
         _?_ cell info        _t_ transpose               _r_ recalculate         _<down>_ below cell
  _<return>_ wrap region      _E_ export table            _D_ toggle debugger     _<left>_ left cell
     _<SPC>_ empty cell       _c_ toggle coordinates      _i_ iterate table      _<right>_ right cell
         _m_ move cell...     _-_ collapse/expand column  _B_ iterate buffer
       _M-w_ copy cell        _<_ collapse all columns    _+_ sum column
       _C-w_ kill cell        _>_ expand all columns      _N_ number rows
       _C-y_ yank & align     _|_ convert from region
_S-<return>_ copy cell down   _v_ vertical align                                                 ^^          _q_ quit
"
  ("E" org-table-export :color blue)
  ("s" org-table-sort-lines)
  ("'" org-table-edit-field nil :color blue)
  ("e" org-table-eval-formula)
  ("r" org-table-recalculate)
  ("i" org-table-iterate)
	("m" hydra-org-table-move-cell/body :color blue)
	("N" tl/org-table-number-rows)
  ("B" org-table-iterate-buffer-tables)
  ("D" org-table-toggle-formula-debugger)
  ("t" org-table-transpose-table-at-point)
	("v" valign-mode)
  ("?" org-table-field-info)
	("M-w" tl/org-table-copy-cell-content)
	("C-w" tl/org-table-kill-cell-content)
	("C-y" (progn 
					 (org-yank)
					 (org-table-align)))
  ("<return>" org-table-wrap-region)
  ("S-<return>" org-table-copy-down)
  ("<SPC>" org-table-blank-field)
	("<tab>" org-cycle)
	("S-<tab>" org-shifttab)
	("-" org-table-toggle-column-width)
	("+" org-table-sum)
	("<" (org-table-toggle-column-width '(4)))
	(">" (org-table-toggle-column-width '(16)))
	("|" org-table-convert-region) 
	("<up>" previous-line)
	("<down>" org-table-next-row)
	("<left>" org-table-previous-field)
	("<right>" org-table-next-field)
  ;; ("n" dfeich/org-table-remove-num-sep :color blue) ; _n_ remove number separators
  ("c" org-table-toggle-coordinate-overlays :color blue)
  ("q" nil :color blue))

hydra-org-table-move-cell

As of v9.3, moving cells is built into org-mode. Before that I used org-table-move-single-cell.

(defhydra hydra-org-table-move-cell (:hint nil
																					 :idle 0.5)
	"
   Move org-table cell
 ╭─────────────────────╯
    _<up>_: move up       _<left>_: move left    
  _<down>_: move down    _<right>_: move left     _q_uit
	"
	("<up>" org-table-move-cell-up)
	("<down>" org-table-move-cell-down)
	("<right>" org-table-move-cell-right)
	("<left>" org-table-move-cell-left)
	("q" hydra-org-table/body :color blue))

hydra-org-markup

(defhydra hydra-org-markup (:hint nil)
	"Org-mode markup"
	("a" tl/insert-org-link-alert "alert" :exit t)
	("b" (org-emphasize ?*) "bold" :exit t)
	("c" tl/insert-org-link-color "color" :exit t)
	("e" tl/insert-org-link-emph "emphasized" :exit t)
	("E" my-sem-hydra-start "editmarks" :exit t)
	("i" (org-emphasize ?/) "italic" :exit t)
	("m" tl/insert-org-link-mono "monospaced" :exit t)
	("s" tl/insert-org-link-smallcaps "small caps" :exit t)
	("t" tl/insert-org-link-term "term" :exit t)
	("u" (org-emphasize ?_) "underlined" :exit t)
	("x" tl/insert-org-link-example "example" :exit t)
	("d" tl/org-replace-link-with-description "delete markup" :exit t)
	("q" nil :color blue))
(global-set-key (kbd "C-c m") 'hydra-org-markup/body)
(defun tl/insert-org-link (type)
	(if (use-region-p)
			(let ((marked (buffer-substring (region-beginning) (region-end))))
				(progn
					(delete-region(region-beginning) (region-end))
					(insert (concat "[[" type "][" marked "]]"))))
		(insert (concat "[[" type "][]]"))
		(backward-char 2))) 
(defun tl/insert-org-link-alert ()
	(interactive)
	(tl/insert-org-link "alert:"))

(defun tl/insert-org-link-term ()
	(interactive)
	(tl/insert-org-link "term:"))

(defun tl/insert-org-link-smallcaps ()
	(interactive)
	(tl/insert-org-link "textsc:"))

(defun tl/insert-org-link-mono ()
	(interactive)
	(tl/insert-org-link "texttt:"))

(defun tl/insert-org-link-emph ()
	(interactive)
	(tl/insert-org-link "emph:"))

(defun tl/insert-org-link-example ()
	(interactive)
	(tl/insert-org-link "bsp:"))

(defun tl/insert-org-link-color ()
	(interactive)
	(tl/insert-org-link
	 (concat "color:"
					 (replace-regexp-in-string "[[:space:]]+" "" (helm-colors))))) 

hydra-org-roam

(defhydra hydra-org-roam (:color teal :hint nil)
	"
    ^^                              ^^                              ^^                          ╭──────────┐
     ^Navigation between notes^       ^Actions inside note^          ^General actions^          │ org-roam │
	╭─^^───────────────────────────────^^─────────────────────────────^^──────────────────────────┴──────────╯
     _b_ Backlinks                    _a+_ Add alias                 _g_ Show graph
     _c_ Capture                      _a-_ Remove alias              _s_ Start UI server    
     _d_ Dailies                       _i_ Insert link               _u_ Update db    
     		 _<_ Previous day   		      _li_ Insert citation link           
         _>_ Next day              	  _t+_ Add tag			             ^^                          _q_:uit   
     _f_ Find note			 				      _t-_ Remove tag                      
  	 _F_ Full-text search              _P_ Promote heading to file
      ^^                               _D_ Demote file to heading
"
  ("q" nil)
	("a+" org-roam-alias-add)
	("a-" org-roam-alias-remove)
	("b" org-roam-buffer-toggle)
  ("c" org-roam-capture)
  ("d" org-roam-dailies-goto-today)
	("<" org-roam-dailies-goto-previous-note :color red)
	(">" org-roam-dailies-goto-next-note :color red)
  ("f" org-roam-node-find)
	;; ("F" tl/search-in-org-roam-files)			; Way too slow!
	("F" (lambda () (interactive) (tl/call-rg-in-directory my-org-roam-directory)))
  ("g" org-roam-graph)
  ("i" org-roam-node-insert)
  ("li" orb-insert-link)
  ("s" org-roam-ui-mode)
	("t+" org-roam-alias-add)
	("t-" org-roam-alias-remove)
	("P" org-roam-extract-subtree)
	("D" org-roam-demote-entire-buffer)
  ("u" org-roam-db-sync)
	)

hydra-deft

(defhydra hydra-deft (:hint nil :columns 3)
"Deft"
("a" deft-archive-file "archive" :exit t)
("d" deft-delete-file "delete" :exit t)
("k" deft-delete-file nil :exit t)
("f" deft-find-file "find" :exit t)
("g" deft-refresh "refresh" :exit t)
("n" deft-new-file "new" :exit t)
("r" deft-rename-file "rename" :exit t)
("l" deft-filter "filter" :exit t)
("/" deft-filter nil :exit t)
("Q" quit-window "quit" :exit t)
("q" nil :color blue)
)

hydra-git-gutter

		;; inspired by https://github.com/abo-abo/hydra/wiki/Git-gutter
		(defhydra hydra-git-gutter (:body-pre (git-gutter-mode 1)
																					:hint nil)
			"
    Git gutter
  ╭────────────╯
   [_j_] next hunk        [_s_]tage hunk     [_q_]uit
   [_k_] previous hunk    [_r_]evert hunk    [_Q_]uit and deactivate git-gutter
    ^ ^                   [_p_]opup hunk
   [_h_] first hunk
   [_l_] last hunk        set start [_R_]evision
	"
			("j" git-gutter:next-hunk)
			("k" git-gutter:previous-hunk)
			("h" (progn (goto-char (point-min))
									(git-gutter:next-hunk 1)))
			("l" (progn (goto-char (point-min))
									(git-gutter:previous-hunk 1)))
			("s" git-gutter:stage-hunk)
			("r" git-gutter:revert-hunk)
			("p" git-gutter:popup-hunk)
			("R" git-gutter:set-start-revision)
			("q" nil :color blue)
			("Q" (progn (git-gutter-mode -1)
									;; git-gutter-fringe doesn't seem to
									;; clear the markup right away
									(sit-for 0.1)
									(git-gutter:clear))
			 :color blue))

hydra-helm

  • State “TODO” from [2018-11-21 Wed 22:06]
  • [ ] needs revision
  • [ ] define hydra for helm-ag
    • [ ] unfold org-mode headings in follow mode

Inspired by https://github.com/abo-abo/hydra/wiki/Helm-2.

(defhydra hydra-helm (:hint nil
														:color pink)
	"
                                                                          ╭──────┐
   Navigation   Other  Sources     Mark             Do             Help   │ Helm │
  ╭───────────────────────────────────────────────────────────────────────┴──────╯
        ^_k_^         _K_       _p_   [_m_] mark         [_v_] view         [_H_] helm help
        ^^↑^^         ^↑^       ^↑^   [_t_] toggle all   [_d_] delete       [_s_] source help
    _h_ ←   → _l_     _c_       ^ ^   [_u_] unmark all   [_f_] follow: %(helm-attr 'follow)
        ^^↓^^         ^↓^       ^↓^    ^ ^               [_y_] yank selection
        ^_j_^         _J_       _n_    ^ ^               [_w_] toggle windows
  --------------------------------------------------------------------------------
        "
	("<tab>" helm-keyboard-quit "back" :exit t)
	("<escape>" nil "quit")
	("\\" (insert "\\") "\\" :color blue)
	("h" helm-beginning-of-buffer)
	("j" helm-next-line)
	("k" helm-previous-line)
	("l" helm-end-of-buffer)
	("g" helm-beginning-of-buffer)
	("G" helm-end-of-buffer)
	("n" helm-next-source)
	("p" helm-previous-source)
	("K" helm-scroll-other-window-down)
	("J" helm-scroll-other-window)
	("c" helm-recenter-top-bottom-other-window)
	("m" helm-toggle-visible-mark)
	("t" helm-toggle-all-marks)
	("u" helm-unmark-all)
	("H" helm-help)
	("s" helm-buffer-help)
	("v" helm-execute-persistent-action)
	("d" helm-persistent-delete-marked)
	("y" helm-yank-selection)
	("w" helm-toggle-resplit-and-swap-windows)
	("f" helm-follow-mode)
	)

hydra-ibuffer

Adapted hydra from https://github.com/abo-abo/hydra/wiki/Ibuffer:

(defhydra hydra-ibuffer-main (:color pink :hint nil)
  "
 ^Navigation^      ^Mark^          ^Actions^          ^View^
-^----------^------^----^----------^-------^----------^----^-------
   _<up>_:   ʌ     _m_: mark       _D_: delete        _g_: refresh
    _RET_: visit   _u_: unmark     _S_: save          _s_: sort
 _<down>_:   v     _*_: specific   _a_: all actions   _/_: filter
-^----------^---^----^----------^-------^----------^----^----------
"
  ("j" ibuffer-forward-line)
  ("<down>" ibuffer-forward-line)
  ("RET" ibuffer-visit-buffer :color blue)
  ("<up>" ibuffer-backward-line)
  ("k" ibuffer-backward-line)

  ("m" ibuffer-mark-forward)
  ("u" ibuffer-unmark-forward)
  ("*" hydra-ibuffer-mark/body :color blue)

  ("D" ibuffer-do-delete)
  ("S" ibuffer-do-save)
  ("a" hydra-ibuffer-action/body :color blue)

  ("g" ibuffer-update)
  ("s" hydra-ibuffer-sort/body :color blue)
  ("/" hydra-ibuffer-filter/body :color blue)

  ("o" ibuffer-visit-buffer-other-window "other window" :color blue)
  ("q" ibuffer-quit "quit ibuffer" :color blue)
  ("." nil "toggle hydra" :color blue))

(defhydra hydra-ibuffer-mark (:color teal :columns 5
                              :after-exit (hydra-ibuffer-main/body))
  "Mark"
  ("*" ibuffer-unmark-all "unmark all")
  ("M" ibuffer-mark-by-mode "mode")
  ("m" ibuffer-mark-modified-buffers "modified")
  ("u" ibuffer-mark-unsaved-buffers "unsaved")
  ("s" ibuffer-mark-special-buffers "special")
  ("r" ibuffer-mark-read-only-buffers "read-only")
  ("/" ibuffer-mark-dired-buffers "dired")
  ("e" ibuffer-mark-dissociated-buffers "dissociated")
  ("h" ibuffer-mark-help-buffers "help")
  ("z" ibuffer-mark-compressed-file-buffers "compressed")
  ("b" hydra-ibuffer-main/body "back" :color blue))

(defhydra hydra-ibuffer-action (:color teal :columns 4
                                :after-exit
                                (if (eq major-mode 'ibuffer-mode)
                                    (hydra-ibuffer-main/body)))
  "Action"
  ("A" ibuffer-do-view "view")
  ("E" ibuffer-do-eval "eval")
  ("F" ibuffer-do-shell-command-file "shell-command-file")
  ("I" ibuffer-do-query-replace-regexp "query-replace-regexp")
  ("H" ibuffer-do-view-other-frame "view-other-frame")
  ("N" ibuffer-do-shell-command-pipe-replace "shell-cmd-pipe-replace")
  ("M" ibuffer-do-toggle-modified "toggle-modified")
  ("O" ibuffer-do-occur "occur")
  ("P" ibuffer-do-print "print")
  ("Q" ibuffer-do-query-replace "query-replace")
  ("R" ibuffer-do-rename-uniquely "rename-uniquely")
  ("T" ibuffer-do-toggle-read-only "toggle-read-only")
  ("U" ibuffer-do-replace-regexp "replace-regexp")
  ("V" ibuffer-do-revert "revert")
  ("W" ibuffer-do-view-and-eval "view-and-eval")
  ("X" ibuffer-do-shell-command-pipe "shell-command-pipe")
  ("b" nil "back"))

(defhydra hydra-ibuffer-sort (:color amaranth :columns 3)
  "Sort"
  ("i" ibuffer-invert-sorting "invert")
  ("a" ibuffer-do-sort-by-alphabetic "alphabetic")
  ("v" ibuffer-do-sort-by-recency "recently used")
  ("s" ibuffer-do-sort-by-size "size")
  ("f" ibuffer-do-sort-by-filename/process "filename")
  ("m" ibuffer-do-sort-by-major-mode "mode")
  ("b" hydra-ibuffer-main/body "back" :color blue))

(defhydra hydra-ibuffer-filter (:color amaranth :columns 4)
  "Filter"
  ("m" ibuffer-filter-by-used-mode "mode")
  ("M" ibuffer-filter-by-derived-mode "derived mode")
  ("n" ibuffer-filter-by-name "name")
  ("c" ibuffer-filter-by-content "content")
  ("e" ibuffer-filter-by-predicate "predicate")
  ("f" ibuffer-filter-by-filename "filename")
  (">" ibuffer-filter-by-size-gt "size")
  ("<" ibuffer-filter-by-size-lt "size")
  ("/" ibuffer-filter-disable "disable")
  ("b" hydra-ibuffer-main/body "back" :color blue))

Automatically open the hydra with ibuffer:

(add-hook 'ibuffer-hook #'hydra-ibuffer-main/body)
;; (global-set-key (kbd "C-x b") 'ibuffer)

hydra-image

(defhydra hydra-image ()
	"Manipulate image"
	("+" imagex-sticky-zoom-in "zoom in")
	("-" imagex-sticky-zoom-out "zoom out")
	("m" imagex-sticky-maximize "maximize")
	("o" imagex-sticky-restore-original "original")
	;; ("S" imagex-sticky-save-image "save")
	("r" imagex-sticky-rotate-right "rotate right")
	("l" imagex-sticky-rotate-left "rotate left")
	("n" image-next-file "next image")
	("p" image-previous-file "previous image"))

hydra-insert-date

Copied from https://github.com/cmcmahan/elisp/blob/master/emacs-hydra.el:

(global-set-key
 (kbd "C-c d")
 (defhydra hydra-insert-date (:exit t :hint none)
   "

      Insert Date
    ╭─────────────╯
      [_1_] ?1?                             [_o_] ?o? 
      [_2_] ?2?                         [_O_] ?O?
      [_3_] ?3?                   
      [_4_] ?4?               [_._] ?.?
      [_5_] ?5?           [_/_] ?/?  
      [_6_] ?6?
      [_7_] ?7?                                                         [_q_] quit
   "
	 ("1" (insert-date "%y%m%d") (format-time-string "%y%m%d"))
	 ("2" (insert-date "%Y-%m-%d") (format-time-string "%Y-%m-%d"))
   ("3" (insert-date) (format-time-string "%Y-%m-%d %a"))
	 ("4" (insert-date "%Y-%m-%d %a %H:%M") (format-time-string "%Y-%m-%d %a %H:%M"))
   ("5" (insert-date "%Y-%m-%dT%T%z") (format-time-string "%Y-%m-%dT%T%z"))
	 ("6" (insert-date "%B %d, %Y") (format-time-string "%B %d, %Y"))
	 ("7" (insert-date "%A, %B %d, %Y") (format-time-string "%A, %B %d, %Y"))
	 ;; ;; inspired from http://ergoemacs.org/emacs/elisp_insert-date-time.html
	 ;; ("4" (progn
	 ;; 				(when (use-region-p) (delete-region (region-beginning) (region-end)))
	 ;; 				(insert (concat
	 ;; 								 (format-time-string "%Y-%m-%dT%T")
	 ;; 								 (funcall (lambda ($x) (format "%s:%s" (substring $x 0 3) (substring $x 3 5))) (format-time-string "%z"))))))
	 ("o" (insert-date "[%Y-%m-%d %a %k:%M]") (format-time-string "[%Y-%m-%d %a %k:%M]"))
	 ("O" (insert-date "<%Y-%m-%d %a %k:%M>") (format-time-string "<%Y-%m-%d %a %k:%M>"))
	 ("/" (insert-date "%d/%m/%Y") (format-time-string "%d/%m/%Y"))
	 ("." (insert-date "%d.%m.%Y") (format-time-string "%d.%m.%Y"))
	 ("q" nil)))
(global-set-key (kbd "C-c i d") 'hydra-insert-date/body)

Copied from https://github.com/cmcmahan/elisp/blob/master/emacs-misc.el:

(defvar insert-time-format "%k:%M"
  "*Format for \\[insert-time]. See `format-time-string' for info on how to format.")
(setq insert-time-format "%k:%M") ;; 08:09

(defvar insert-date-format "%Y-%m-%d"
  "*Format for \\[insert-date]. See `format-time-string' for info on how to format.")
(setq insert-date-format "%Y-%m-%d %a") ;; 2015-03-26 Thu

(defun insert-time (&optional time-format)
  "Insert the current time. Optional time format defaults to `insert-time-format'."
  (interactive "*")
  (let ((tformat (or time-format insert-time-format)))
		(when (use-region-p)
			(delete-region (region-beginning) (region-end)))
    (insert (concat (format-time-string tformat (current-time)) ; " " ; removed by TL
										))))

(defun insert-date (&optional date-format)
  "Insert the current date. Option format defaults to  `insert-date-format'."
  (interactive "*")
  (insert-time (or date-format insert-date-format)))

(defun insert-date-time ()
  "Insert the current date formatted with `insert-date-format',
then a space, then the current time formatted with
`insert-time-format'."
  (interactive "*")
  (insert-time
   (concat insert-date-format " " insert-time-format)))

(defun idt ()
  "Shortcut to `insert-date-time'"
(interactive)
(insert-date-time))

(defun mp-insert-date ()
  (interactive)
  (insert (format-time-string "%x")))
 
(defun mp-insert-time ()
  (interactive)
  (insert (format-time-string "%X")))
 
;; (global-set-key (kbd "C-c i d") 'mp-insert-date)
;; (global-set-key (kbd "C-c i t") 'mp-insert-time)

(defun insert-current-file-name ()
  (interactive)
  (insert (buffer-file-name (current-buffer))))

hydra-insert-file-name

(defhydra hydra-insert-file-name (:exit t :hint nil)
	"

      Insert File Name
    ╭──────────────────╯
      [_._] ../relative/path      [_w_] file name
      [_/_] /absolute/path   
      ^^                                          [_q_] quit
   "
	("/" (insert (read-file-name "")))
	("." (insert (file-relative-name (read-file-name "")
																	 (file-name-directory (buffer-file-name)))))
  ("w" (insert (file-name-nondirectory (read-file-name ""))))
	("q" nil))
(global-set-key (kbd "C-c i f") 'hydra-insert-file-name/body)

hydra-jump

(defhydra hydra-jump (:color blue :columns 3)
	"Jump to"
	("l" goto-line "line number")
	("g" goto-line)
	("TAB" move-to-column "column number")
	("L" ace-jump-line-mode "visible line")
	("c" ace-jump-mode "visible char")
	("d" dumb-jump-go "definition")
	("D" dumb-jump-go-prompt "definition(prompt)")
	("f" dumb-jump-quick-look "definition (quick look)")
	("b" dumb-jump-back "definition (back)"))

hydra-khardel

Hydra for khardel.

(defhydra hydra-khardel (:hint nil :columns 3)
	"Khardel – Frontend for Khard"
	("i" khardel-insert-email "insert email address" :exit t)
	("e" khardel-edit-contact "edit contact" :exit t)
	("n" khardel-new-contact "new contact" :exit t)
	("s" tl/vdirsyncer-sync-contacts "sync contacts" :exit t)
	("q" nil :color blue)
	)

hydra-latex

(defhydra hydra-latex-main (:hint nil :pre (outline-minor-mode 1))
	"
  ^^                          ^^                         ^^                      ^^          ╭────────┐   
   ^Navigation^               ^Text^                     ^Outline^               ^Compile^   │ LaTeX  │
╭─^^──────────────────────────^^─────────────────────────^^──────────────────────^^──────────┴────────╯
   _<up>_ previous heading    _m_ insert macro          _S-<tab>_ cycle all      _c_ compile
 _<down>_ next heading        _s_ insert section          _<tab>_ cycle at point _l_ show log
      _=_ table of contents   _e_ insert environment     _M-<up>_ move up        _r_ compile region
       ^^                    _!e_ change environment   _M-<down>_ move down      _v_ view output
       ^^                     _d_ delete...            _M-<left>_ promote        ___ set master file
       ^^                     _f_ font...             _M-<right>_ demote         _b_ generate bib-file
       ^^                    _#a_ align                       _o_ folding...
"
	("<up>" tl/reftex-previous :exit nil)
	("<down>" tl/reftex-next :exit nil)
	("p" tl/reftex-previous :exit nil)
	("n" tl/reftex-next :exit nil)
	("=" reftex-toc :exit t)
	("S-<tab>" (lambda () (interactive)
							 (save-excursion
								 (beginning-of-buffer)
								 (search-forward "\\begin{document}")
								 (outline-cycle)
								 (recenter-top-bottom))) :exit nil)
	("<tab>" outline-cycle :exit nil)
	("M-<up>" outline-move-subtree-up :exit nil)
	("M-<down>" outline-move-subtree-down :exit nil)
	("M-<left>" outline-promote :exit nil)
	("M-<right>" outline-demote :exit nil)
	("d" hydra-latex-delete/body :exit t)
	("e" LaTeX-environment :exit t)
	("!e" (lambda () (interactive) (LaTeX-environment '4)) :exit t)
	("m" TeX-insert-macro :exit t)
	("f" hydra-latex-fonts/body :exit t)
	("#a" align-current :exit t)
	("b" reftex-create-bibtex-file :exit t)
	("c" TeX-command-master :exit t)
	("l" TeX-recenter-output-buffer :exit t)
	("r" TeX-command-region :exit t)
	("s" LaTeX-section :exit t)
	("o" hydra-latex-folding/body :exit t)
	("v" TeX-view :exit t)
	("_" TeX-master-file-ask :exit t)
	("q" nil :color blue)
	)

A hydra for code folding with AUCTeX:

(defhydra hydra-latex-folding (:hint nil :body-pre (TeX-fold-mode 1))
	"LaTeX folding"
	("b" TeX-fold-clearout-buffer "remove folding in buffer" :exit t)
	("B" TeX-fold-buffer "activate folding in buffer" :exit t)
	("o" TeX-fold-dwim "fold dwim" :exit t)
	("Q" TeX-fold-mode "quit TeX-fold-mode" :exit t)
	("q" nil :color blue)
	)

A hydra for code deletion with AUCTeX:

(defhydra hydra-latex-delete (:hint nil)
	"LaTeX deletion"
	("f" TeX-deletefont "font" :exit t)
	("e" TeX-delete-environment "environment" :exit t)
	("m" TeX-delete-macro "macro" :exit t)
	("t" TeX-clean "temporary files" :exit t)
	("q" nil :color blue)
	)

A hydra for font selection with AUCTeX:

(defhydra hydra-latex-fonts (:hint nil :columns 3)
	"LaTeX fonts"
	("b" TeX-bold "bold" :exit t)
	("i" TeX-italic "italic" :exit t)
	("t" TeX-typewriter "typewriter" :exit t)
	("s" TeX-smallcaps "smallcaps" :exit t)
	("e" TeX-emphasis "emphasis" :exit t)
	("d" TeX-deletefont "delete" :exit t)
	("!b" TeX-bold-replace "replace with bold" :exit t)
	("!i" TeX-italic-replace "replace with italic" :exit t)
	("!t" TeX-typewriter-replace "replace with typewriter" :exit t)
	("!s" TeX-smallcaps-replace "replace with smallcaps" :exit t)
	("!e" TeX-emphasis-replace "replace with emphasis" :exit t)
	("q" nil :color blue)
	)

hydra-macro

Inspired by: https://github.com/abo-abo/hydra/wiki/Macro

(defhydra hydra-macro (:hint nil :color pink :pre 
                             (when defining-kbd-macro
															 (kmacro-end-macro 1)))
  "
  ^Create-Cycle^    ^Basic^          ^Counter^        ^Save^          ^Edit^
╭────────────────────────────────────────────────────────────────────────────╯
    ^_C-n_^          [_e_] execute    [_ci_] insert    [_N_] name      [_'_] previous
     ^^↑^^           [_r_] on region  [_cs_] set       [_K_] key       [_,_] last
 _(_ ←   → _)_       [_o_] edit       [_ca_] add       [_R_] register     
     ^^↓^^           [_d_] delete     [_cf_] format    [_D_] defun
    ^_C-p_^          [_m_] step
    ^^   ^^          [_t_] swap on ring 
"
  ("j" kmacro-start-macro :color blue)
	("(" kmacro-start-macro :color blue)
  ("l" kmacro-end-or-call-macro-repeat)
	(")" kmacro-end-or-call-macro-repeat)
  ("i" kmacro-cycle-ring-previous)
	("C-p" kmacro-cycle-ring-previous)
  ("k" kmacro-cycle-ring-next)
	("C-n" kmacro-cycle-ring-next)
  ("r" apply-macro-to-region-lines)
  ("d" kmacro-delete-ring-head)
  ("e" kmacro-end-or-call-macro-repeat)
  ("o" kmacro-edit-macro-repeat)
  ("m" kmacro-step-edit-macro)
  ("t" kmacro-swap-ring)
  ("ci" kmacro-insert-counter)
  ("cs" kmacro-set-counter)
  ("ca" kmacro-add-counter)
  ("cf" kmacro-set-format)
  ("N" kmacro-name-last-macro)
  ("K" kmacro-bind-to-key)
  ("D" insert-kbd-macro)
  ("R" kmacro-to-register)
  ("'" kmacro-edit-macro)
  ("," edit-kbd-macro)
  ("q" nil :color blue))

hydra-markdown

(defhydra hydra-markdown (:hint nil :color blue)
	"
     ^^                           ^^                    ^^              ╭───────────────┐   
     ^Heading^                    ^Text^                ^Navigation^    │ markdown-mode │
	╭──^^───────────────────────────^^────────────────────^^──────────────┴───────────────╯ 
    _M-<right>_ demote          _b_ bold                  
     _M-<left>_ promote         _i_ italic                
       _M-<up>_ move up         _s_ strike through        
     _M-<down>_ move down       _l_ link                  
            _T_ generate TOC    _>_ blockquote            
            ^^                  _`_ code                  
            ^^                  _c_ code block            
            ^^                  _#_ table                 
"
	("b" markdown-insert-bold)
	("i" markdown-insert-italic)
	("s" markdown-insert-strike-through)
	("l" markdown-insert-link)
	(">" markdown-insert-blockquote)
	("`" markdown-insert-code)
	("c" markdown-insert-gfm-code-block)
	("#" hydra-markdown-table/body)
	("M-<right>" markdown-demote :color pink)
	("M-<left>" markdown-promote :color pink)
	("M-<up>" markdown-move-up :color pink)
	("M-<down>" markdown-move-down :color pink)
	("T" markdown-toc-generate-toc)
	("q" nil :color blue)
	)

hydra-markdown-table

(defhydra hydra-markdown-table (:hint nil :color blue)
	"
     ^^                           ^^                          ^^                       ╭─────────────────────┐   
     ^Navigation^                 ^Insert & Delete^            ^Create & Convert      │ markdown-mode table │
	╭──^^──────────────────────────^^───────────────────────────^^───────────────────────┴─────────────────────╯ 
       _<up>_ previous row              _i_ insert table   
     _<down>_ next row           _M-S-<up>_ delete row         _t_ transpose table  
     _<left>_ left column      _M-S-<down>_ insert row         _|_ convert region  
    _<right>_ right column     _M-S-<left>_ delete column      _a_ align table
    ^^                        _M-S-<right>_ insert column
"
	("<up>" previous-line :color pink)
	("<down>" markdown-table-next-row :color pink)	
	("<left>" markdown-table-backward-cell :color pink)
	("<right>" markdown-table-forward-cell :color pink)
	("M-<right>" markdown-demote :color pink)
	("M-<left>" markdown-promote :color pink)
	("M-<up>" markdown-move-up :color pink)
	("M-<down>" markdown-move-down :color pink)
	("M-S-<down>" markdown-table-insert-row :color pink)
	("M-S-<up>" markdown-table-delete-row :color pink)
	("M-S-<left>" markdown-table-delete-column :color pink)
	("M-S-<right>" markdown-table-insert-column :color pink)
	("i" markdown-insert-table)
	("t" markdown-table-transpose)
	("|" markdown-table-convert-region)
	("a" markdown-table-align)
	("q" nil :color blue)
	)

hydra-mu4e

hydra-mu4e-headers

(defhydra hydra-mu4e-headers (:hint none :color blue)
	"
     ^^                           ^^                          ^^                             ╭─────────────┐   
     ^Header^                    ^Navigation^                ^Filter^                        │ mu4e-header │
	╭──^^───────────────────────────^^──────────────────────────^^─────────────────────────────┴─────────────╯ 
     _C_ Compose                 _p_ Previous _[_ unread       _g_ Refresh                       _H_ Help
     _R_ Reply                   _n_ Next _]_ unread           _s_ Search                        _q_ quit
     _F_ Forward                 _j_ Jump to mailbox         _b_ Search with bookmarks        
     _N_ Edit as new             _;_ Switch context   _M-<left>_ Previous search
     ^Mark for^                  ^^                  _M-<right>_ Next search       
         _d_/_=_ Trash/Untrash                               _f_ Search with helm-mu
         _!_/_?_ Read/Unread      _Sv_ ?Sv? Split vertic.       _c_ Contacts					
         _+_/_-_ Flag/Unflag      _Sh_ ?Sh? Split horiz.       _Pt_ Toggle threading   	 	    
     _u_ Unmark                	^^			                    _Pr_ Toggle show related		
     _U_ Unmark all            	^^                           _O_ Change Sorting															
     _T_ Mark whole thread       _._ View raw message
     _a_ Action   
     _x_ Execute marks
"         
	("p" mu4e-headers-prev :color pink)
	("n" mu4e-headers-next :color pink)
	("s" mu4e-search)
	("f" helm-mu)
	("g" mu4e-search-rerun)
	("j" mu4e~headers-jump-to-maildir)
	("b" mu4e-search-bookmark)
	("c" helm-mu-contacts)
	("C" mu4e-compose-new)
	("R" mu4e-compose-reply)
	("F" mu4e-compose-forward)
	("N" mu4e-compose-supersede)
	("Pt" mu4e-headers-toggle-threading)
	("Pr" mu4e-headers-toggle-include-related)
	("O" mu4e-search-change-sorting)
	("Sv" (setq mu4e-split-view 'vertical) (if (eq 'vertical mu4e-split-view) "[x]" "[ ]"))
	("Sh" (setq mu4e-split-view 'horizontal) (if (eq 'horizontal mu4e-split-view) "[x]" "[ ]"))
	("H" mu4e-display-manual)
	("+" mu4e-headers-mark-for-flag)
	("-" mu4e-headers-mark-for-unflag)
	("d" mu4e-headers-mark-for-trash)
	("=" mu4e-headers-mark-for-untrash)
	("u" mu4e-headers-mark-for-unmark)
	("U" mu4e-headers-unmark-all)
	("T" mu4e-headers-mark-thread)
	("x" mu4e-mark-execute-all)
	("!" mu4e-headers-mark-for-read)
	("?" mu4e-headers-mark-for-unread)
	(";" mu4e-context-switch)
	("." mu4e-view-raw-message)
	("[" mu4e-headers-prev-unread :color pink)
	("]" mu4e-headers-next-unread :color pink)
	("M-<left>" mu4e-search-prev :color pink)
  ("M-<right>" mu4e-search-next :color pink)
	("a" mu4e-headers-mark-for-action)
	("q" nil :color blue)
	)

hydra-mu4e-view

(defhydra hydra-mu4e-view (:hint nil :color blue)
	"
     ^^                           ^^                          ^^                             ╭───────────┐   
     ^Message^                   ^Navigation^                ^Filter^                          │ mu4e-view │
	╭──^^───────────────────────────^^──────────────────────────^^─────────────────────────────┴───────────╯ 
     _C_ Compose                 _p_ Previous _[_ unread       _g_ Refresh                       _H_ Help
     _R_ Reply                   _n_ Next _]_ unread           _s_ Search                        _q_ quit
     _F_ Forward                 _j_ Jump to mailbox         _b_ Search with bookmarks
     _N_ Edit as new             _;_ Switch context          _c_ Contacts
     ^Mark for^                  ^^                          
         _d_/_=_ Trash/Untrash     _z_ Detach view           _Pt_ Toggle threading     
         _!_/_?_ Read/Unread       _._ View raw              _Pr_ Toggle show related  
         _+_/_-_ Flag/Unflag       ^^                         _O_ Change Sorting
     _u_ Unmark                  _e_ Save attachment 
     _U_ Unmark all              _o_ Save inline image
     _T_ Mark whole thread
     _a_ Action
     _A_ Action on attachment
     _x_ Execute marks
"
	("p" mu4e-view-headers-prev :color pink)
	("n" mu4e-view-headers-next :color pink)
	("s" mu4e-search)
	("g" mu4e-search-rerun)
	("j" mu4e~headers-jump-to-maildir)
	("b" mu4e-search-bookmark)
	("c" helm-mu-contacts)
	("C" mu4e-compose-new)
	("e" mu4e-view-save-attachments)
	("o" image-save)
	("R" mu4e-compose-reply)
	("F" mu4e-compose-forward)
	("N" mu4e-compose-supersede)
	("O" mu4e-search-change-sorting)
	("Pt" mu4e-headers-toggle-threading)
	("Pr" mu4e-headers-toggle-include-related)
	("+" mu4e-view-mark-for-flag)
	("-" mu4e-view-mark-for-unflag)
	("d" mu4e-view-mark-for-trash)
	("=" mu4e-view-mark-for-untrash)
	("u" mu4e-view-unmark)
	("U" mu4e-view-unmark-all)
	("T" mu4e-view-mark-thread)
	("x" mu4e-view-marked-execute)
  ("z" tl/mu4e-detach-and-view-message-in-new-frame)
	("!" mu4e-view-mark-for-read)
	("?" mu4e-view-mark-for-unread)
	(";" mu4e-context-switch)
	("." mu4e-view-raw-message)
	("[" mu4e-view-headers-prev-unread :color pink)
	("]" mu4e-view-headers-next-unread :color pink)
	("a" mu4e-view-action)
	("A" mu4e-view-mime-part-action)
	("H" mu4e-display-manual)
	("q" nil :color blue)
	)

hydra-mu4e-compose

  • State “TODO” from [2024-04-02 Tue 16:04]
  • [ ] Add keys for all address fields and more
(defhydra hydra-mu4e-compose (:hint nil :color blue)
	"
     ^^                        ^^                          ^^                      ╭───────────┐   
     ^Actions^                 ^Attach^                    ^Contents^              │ mu4e-view │
	╭──^^────────────────────────^^──────────────────────────^^──────────────────────┴───────────╯ 
     _C-c C-c_ Send            _af_ File                   _S_ Sign                   _H_ Help
     _C-c C-d_ Pospone         _ab_ Buffer                 _C_ Encrypt                _q_ quit
     _C-c C-j_ Delay           _ac_ Clipoard               _E_ Sign & encrypt
     _C-c C-k_ Kill            _aC_ Captured message      _is_ Insert screenshot
     ^^                        ^^                         _ic_ Insert text with CAESAR13
"         
	("C-c C-c" message-send-and-exit)
	("C-c C-d" message-dont-send)
	("C-c C-j" gnus-delay-article)
	("C-c C-k" mu4e-message-kill-buffer)
	("af" mml-attach-file)
	("ab" mml-attach-buffer)
	("ac" my/mu4e-attach-image-from-clipboard)
	("aC" mu4e-compose-attach-captured-message)
	("S" mml-secure-sign) 
   ("C" mml-secure-encrypt)
	 ("E" mml-secure-message-sign-encrypt)
	("is" message-insert-screenshot)
   ("ic" message-caesar-buffer-body)
	("H" mu4e-display-manual)
	("q" nil :color blue)
	)

hydra-flycheck

;; inspired by https://github.com/abo-abo/hydra/wiki/Flycheck
(defhydra hydra-flycheck
  (:pre (flycheck-list-errors)
				:post (quit-windows-on "*Flycheck errors*")
				:hint nil)
	"
  Errors
╭────────╯
"
	("f" flycheck-error-list-set-filter "Filter")
	("j" flycheck-next-error nil)
	("<down>" flycheck-next-error "Next")
	("k" flycheck-previous-error nil)
	("<up>" flycheck-previous-error "Previous")
	("[" flycheck-first-error "First")
	("]" (progn (goto-char (point-max)) (flycheck-previous-error)) "Last")
	("Q" (flycheck-mode -1) "Quit flycheck-mode") 
	("q"  nil))

hydra-flyspell

(defhydra hydra-flyspell (:pre (when (not flyspell-mode)
																 (flyspell-mode +1)
																 (flyspell-buffer))
															 :color blue
															 :hint nil
															 :idle 0.2)
    "
   ^^                            ^^                             ╭──────────┐
   ^^Flyspell                    ^^Ispell                       │ Flyspell │
╭──^^────────────────────────────^^─────────────────────────────┴──────────╯
    [_<up>_] previous error       [_b_] check buffer
       [_c_] correct word         [_w_] check word           
  [_<down>_] next error           [_d_] change dictionary   
       [_f_] toggle flyspell                             [_q_] quit window 
       [_p_] toggle prog mode                            [_Q_] quit flyspell
      "
    ("w" ispell-word :color red)
    ("i" ispell)
		("b" ispell-buffer)
    ("d" ispell-change-dictionary)
    ("f" flyspell-mode)
    ("p" flyspell-prog-mode)
    ;; ("c" flyspell-auto-correct-word)
		("c" flyspell-correct-word-generic)
    ;; ("<up>"  flyspell-previous-and-ispell-word :color red)
		;; ("<down>" flyspell-next-and-ispell-word :color red)
    ;; ("k"  flyspell-previous-and-ispell-word :color red)
		;; ("j" flyspell-next-and-ispell-word :color red)
    ("<up>"  flyspell-goto-previous-and-correct :color red)
		("<down>" flyspell-goto-next-and-correct :color red)
    ("k"  flyspell-goto-previous-and-correct :color red)
		("j" flyspell-goto-next-and-correct :color red)
    ("q" nil)
    ("Q" (flyspell-mode -1)))

hydra-compilation-error

;; inspired by https://github.com/abo-abo/hydra/wiki/Compilation
(defhydra hydra-compilation-error (:hint nil :body-pre (hydra-compilation-error-body-pre))
	"
	    Compilation errors
    ╭────────────────────╯
	      [_<up>_] previous error   [_l_] list errors    [_q_]uit
	    [_<down>_] next error       
	"
	("`" hydra-compilation-error-next)
	("j" hydra-compilation-error-next)
	("<down>" hydra-compilation-error-next)
	("k" hydra-compilation-error-previous)
	("<up>" hydra-compilation-error-previous)
	("l" hydra-compilation-error-list :exit t)
	("o" hydra-compilation-error-list :exit t)
	("q" hydra-compilation-error-quit nil :color blue))

(defun hydra-compilation-error-next ()
	(interactive)
  (when (string-equal (buffer-mode) "latex-mode")
		(TeX-next-error 1)))

(defun hydra-compilation-error-previous ()
	(interactive)
  (when (string-equal (buffer-mode) "latex-mode")
		(TeX-previous-error 1)))

(defun hydra-compilation-error-list ()
	(interactive)
  (when (string-equal (buffer-mode) "latex-mode")
		(TeX-error-overview)))

(defun hydra-compilation-error-quit ()
	(interactive)
  (when (string-equal (buffer-mode) "latex-mode")
		(delete-other-windows)))

(defun hydra-compilation-error-body-pre ()
	(interactive)
  (when (string-equal (buffer-mode) "latex-mode")
		(TeX-next-error 1)))

hydra-multiple-cursors

;; inspired by https://github.com/abo-abo/hydra/wiki/multiple-cursors
(defhydra hydra-multiple-cursors (:hint nil :idle 0.2)
	"
    		 	 ^Up^                   ^Down^            ^Other^
   ╭───^^───────────────────────────────^^──────────────^^───────────────────╯
    [_S-<up>_] Next      [_S-<down>_] Next         [_l_] Edit lines
      [_<up>_] Skip        [_<down>_] Skip         [_a_] Mark all
    [_M-<up>_] Unmark    [_M-<down>_] Unmark       [_r_] Mark by regexp
            [_SPC_] Mark region ^^      ^^[C-S-<mouse1>] Mark with mouse    
        ^   ^              ^^                      [_q_] Quit hydra     [_Q_] Quit mc-mode
	"
	("l" mc/edit-lines :exit t)
	("a" mc/mark-all-like-this :exit t)
	("n" mc/mark-next-like-this)
	("N" mc/skip-to-next-like-this)
	("M-n" mc/unmark-next-like-this)
	("S-<down>" mc/mark-next-like-this)
	("<down>" mc/skip-to-next-like-this)
	("M-<down>" mc/unmark-next-like-this)
  ("C-S-<mouse-1>" 'mc/add-cursor-on-click)
	("p" mc/mark-previous-like-this)
	("P" mc/skip-to-previous-like-this)
	("M-p" mc/unmark-previous-like-this)
	("SPC" set-rectangular-region-anchor :exit t)
	("S-<up>" mc/mark-previous-like-this)
	("<up>" mc/skip-to-previous-like-this)
	("M-<up>" mc/unmark-previous-like-this)
	("r" mc/mark-all-in-region-regexp :exit t)
  ("Q" (multiple-cursors-mode -1) :exit t)
	("q" nil))
(global-set-key (kbd "C-<") 'hydra-multiple-cursors/body)
(global-set-key (kbd "M-+") 'hydra-multiple-cursors/body)
(global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)

hydra-highlight-changes

(defhydra hydra-highlight-changes
	(:body-pre (highlight-changes-mode +1)
						 :hint nil
						 :idle 0.5)
	"
    Highlight changes
  ╭───────────────────╯
	  _<up>_: next change       _q_uit
  _<down>_: previous change   _Q_uit and turn off highlighting 
	"
	("<down>" highlight-changes-next-change)
	("<up>" highlight-changes-previous-change)
	("<right>" highlight-changes-next-change)
	("<left>" highlight-changes-previous-change)
	("j" highlight-changes-next-change)
	("k" highlight-changes-previous-change)
	("n" highlight-changes-next-change)
	("p" highlight-changes-previous-change)
	("Q" (highlight-changes-mode -1) :color blue)
	("q" nil :color blue))

hydra-highlight-symbol

		(defhydra hydra-highlight-symbol
			(:pre (highlight-symbol-at-point) 
     :hint nil
			 :idle 0.5)
			"
  Highlight symbol
╭──────────────────╯
   _<up>_: previous occurrence    _r_: replace occurrences     _q_uit 
 _<down>_: next occurrence        
	"
			("<down>" highlight-symbol-next)
			("<up>" highlight-symbol-prev)
			("<right>" highlight-symbol-next)
			("<left>" highlight-symbol-prev)
			("j" highlight-symbol-next)
			("k" highlight-symbol-prev)
			("n" highlight-symbol-next)
			("p" highlight-symbol-prev)
			("c" highlight-symbol-count)
			("r" highlight-symbol-query-replace)
			("l" highlight-symbol-list-all)
			("q" highlight-symbol-remove-all :color blue))

hydra-pdftools

Hydra taken from https://github.com/abo-abo/hydra/wiki/PDF-Tools#my-hydra-for-pdf-tools:

(defhydra hydra-pdftools (:color blue :hint nil)
	"
                                                                                         ╭───────────┐
       Move     History       Scale/Fit       Annotations            Search/Link    Do   │ PDF Tools │
   ╭─────────────────────────────────────────────────────────────────────────────────────┴───────────╯
         ^^_g_^^      _B_/_M-<left>_    ^↧^    _+_    ^ ^     [_C-c C-a l_] list       [_f_] search     [_u_] revert buffer
         ^^^↑^^^         ^↑^^^          _H_    ^↑^  ↦ _W_ ↤   [_C-c C-a m_] markup...  [_o_] outline    [_i_] info
         ^^_p_^^        ^ ^^^           ^↥^    _0_    ^ ^     [_C-c C-a t_] text       [_F_] link       [_d_] dark mode
         ^^^↑^^^         ^↓^^^        ╭─^─^─┐  ^↓^  ╭─^ ^─┐   [_C-c C-a D_] delete     [_f_] search link
    _h_ ←pag_e_→ _l_  _N_/_M-<right>_ │ _P_ │  _-_    _b_     [_C-c C-a a_] attacments
         ^^^↓^^^         ^ ^^^        ╰─^─^─╯  ^ ^  ╰─^ ^─╯   [_C-w_]  copy
         ^^_n_^^         ^ ^^^        _r_eset slice box
         ^^^↓^^^
         ^^_G_^^
   --------------------------------------------------------------------------------
        "
	("\\" hydra-master/body "back")
	("<ESC>" nil "quit")
	("q" nil nil)
	("C-c C-a l" pdf-annot-list-annotations)
	("C-c C-a D" pdf-annot-delete)
	("C-c C-a a" pdf-annot-attachment-dired)
	("C-c C-a m" hydra-pdftools-markup/body)
	("C-c C-a t" pdf-annot-add-text-annotation)
	("C-w"  pdf-view-kill-ring-save)
	("+" pdf-view-enlarge :color red)
	("-" pdf-view-shrink :color red)
	("0" pdf-view-scale-reset)
	("H" pdf-view-fit-height-to-window)
	("W" pdf-view-fit-width-to-window)
	("P" pdf-view-fit-page-to-window)
	("n" pdf-view-next-page-command :color red)
	("p" pdf-view-previous-page-command :color red)
	("d" pdf-view-dark-minor-mode)
	("b" pdf-view-set-slice-from-bounding-box)
	("r" pdf-view-reset-slice)
	("g" pdf-view-first-page)
	("G" pdf-view-last-page)
	("e" pdf-view-goto-page)
	("o" pdf-outline)
	("f" pdf-occur)
	("s" isearch-forward)
	("i" pdf-misc-display-metadata)
	("u" pdf-view-revert-buffer)
	("F" pdf-links-action-perfom)
	;; ("f" pdf-links-isearch-link)
	("B" pdf-history-backward :color red)
	("M-<left>" pdf-history-backward :color red)
	("N" pdf-history-forward :color red)
	("M-<right>" pdf-history-forward :color red)
	("l" image-forward-hscroll :color red)
	("h" image-backward-hscroll :color red))

(with-eval-after-load 'pdf-tools 
	(define-key pdf-view-mode-map (kbd "C-f") 'pdf-occur))
(defhydra hydra-pdftools-markup (:color blue)
	"PDFTools Markup"
	("h" pdf-annot-add-highlight-markup-annotation "highlight")
	("s" pdf-annot-add-squiggly-markup-annotation "squiggly")
	("u" pdf-annot-add-underline-markup-annotation "underline")
	("o" pdf-annot-add-strikeout-markup-annotation "strikeout")
	("*" pdf-annot-add-markup-annotation "free choice")
	)

hydra-pomidor

(defhydra hydra-pomidor (:color blue)
	"Pomidor"
	("<return>" pomidor-stop "next")
	("<space>" pomidor-break "pause")
	("R" pomidor-reset "reset")
	("q" quit-window "quit window")
	("Q" pomidor-quit "quit pomidor")
)

hydra-position-register

(defhydra hydra-position-register (:hint nil :columns 4)
	"Position register"
	("i" iregister-point-to-register "insert position" :exit t)
	("j" helm-register "jump to position" :exit t)
	("n" iregister-jump-to-next-marker "next position" :exit t)
	("p" iregister-jump-to-previous-marker "previous position" :exit t)
	("d" (lambda () (interactive) 
				 (setq register-alist '())
				 (message "Register is empty now.")) "clean register" :exit t)
	("q" nil :color blue))

hydra-load-theme

Switch between installed themes.

Copied from http://www.superloopy.io/articles/2017/hydra-theme-switcher.html:

(defun sb/disable-all-themes ()
  (interactive)
  (mapc #'disable-theme custom-enabled-themes))

(defun sb/load-theme (theme)
  "Enhance `load-theme' by first disabling enabled themes."
  (sb/disable-all-themes)
  (load-theme theme))

(setq sb/hydra-selectors
      "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")

(defun sb/sort-themes (themes)
  (sort themes (lambda (a b) (string< (symbol-name a) (symbol-name b)))))

(defun sb/hydra-load-theme-heads (themes)
  (mapcar* (lambda (a b)
             (list (char-to-string a) `(sb/load-theme ',b) (symbol-name b)))
           sb/hydra-selectors themes))

(bind-keys ("C-c w t" .
            (lambda ()
              (interactive)
              (call-interactively
               (eval `(defhydra sb/hydra-select-themes (:hint nil :color pink)
                        "Select Theme"
                        ,@(sb/hydra-load-theme-heads (sb/sort-themes (custom-available-themes)))
                        ("DEL" (sb/disable-all-themes))
                        ("RET" nil "done" :color blue)))))))

I’ve added this to make it fit into hydra-f3:

(defun hydra-load-theme/body ()
	(interactive)
	(call-interactively
	 (eval `(defhydra sb/hydra-select-themes (:hint nil :color pink)
						"Select Theme"
						,@(sb/hydra-load-theme-heads (sb/sort-themes (custom-available-themes)))
						("DEL" (sb/disable-all-themes))
						("q" nil "quit" :color blue))))
)

hydra-search

(defhydra hydra-search (:color blue :hint nil)
  "
  ^^                     ^^                      ^^                   ^^          ╭────────┐   
   ^in buffer^            ^across buffers^        ^grep-like^          ^file^     │ Search │
╭─^^─────────────────────^^──────────────────────^^───────────────────^^──────────┴────────╯
   _b_ helm-swoop         _B_ all buffers         _a_ helm-ag          _f_ helm-find
   _s_ isearch forward    _M_ all buffers         _g_ git grep         _c_ helm-recoll
   _r_ isearch backward   ^^  with current mode   _p_ projectile grep
   _o_ occur              _O_ all buffers         _R_ ripgrep
   ^^                     ^^                      _P_ pdfgrep 
"
	("a" helm-ag)
	("b" helm-swoop)
	("B" helm-multi-swoop-all)
	("c" helm-recoll-all)
	("M" helm-multi-swoop-current-mode)
	("f" helm-find)
  ("g" helm-grep-do-git-grep)
  ("O" multi-occur)
  ("o" occur)
  ("p" projectile-grep)
	("P" pdfgrep)
  ("r" isearch-backward)
  ("R" rg)
	("s" isearch-forward)
	)
(global-set-key (kbd "C-S-f") 'hydra-search/body)

hydra-smerge

From https://github.com/alphapapa/unpackaged.el#hydra.

(defhydra unpackaged/smerge-hydra
    (:color pink :hint nil :post (smerge-auto-leave))
    "
^Move^       ^Keep^               ^Diff^                 ^Other^
^^-----------^^-------------------^^---------------------^^-------
_n_ext       _b_ase               _<_: upper/base        _C_ombine
_p_rev       _u_pper              _=_: upper/lower       _r_esolve
^^           _l_ower              _>_: base/lower        _k_ill current
^^           _a_ll                _R_efine
^^           _RET_: current       _E_diff
"
    ("n" smerge-next)
    ("p" smerge-prev)
    ("b" smerge-keep-base)
    ("u" smerge-keep-upper)
    ("l" smerge-keep-lower)
    ("a" smerge-keep-all)
    ("RET" smerge-keep-current)
    ("\C-m" smerge-keep-current)
    ("<" smerge-diff-base-upper)
    ("=" smerge-diff-upper-lower)
    (">" smerge-diff-base-lower)
    ("R" smerge-refine)
    ("E" smerge-ediff)
    ("C" smerge-combine-with-next)
    ("r" smerge-resolve)
    ("k" smerge-kill-current)
    ("ZZ" (lambda ()
            (interactive)
            (save-buffer)
            (bury-buffer))
     "Save and bury buffer" :color blue)
    ("q" nil "cancel" :color blue))

hydra-tags

(defhydra hydra-tags (:color red)
	"Tags"
	("." helm-etags-select "goto definition" :color blue)
	("q" nil "cancel" :color blue))

hydra-transpose

Taken from https://github.com/abo-abo/hydra/wiki/Emacs

(defhydra hydra-transpose (:color red)
	"Transpose"
	("d" hydra-drag-stuff/body "drag stuff" :color blue)
  ("D" xah-fix-datetime "date")
	("c" transpose-chars "characters")
	("w" transpose-words "words")
	("x" transpose-sexps "expressions")
	("l" transpose-lines "lines")
	("s" transpose-sentences "sentences")
	("p" transpose-paragraphs "paragraphs")
	("f" transpose-frame "frame")
	("o" org-transpose-words "Org-words")
	("e" org-transpose-element "Org-elements")
	("tp" org-table-transpose-table-at-point "Org-table at point")
	("tc" hydra-org-table-move-cell/body "Org-table cell")
	("C-t" nil nil :color blue)
	("q" nil "cancel" :color blue))
(global-set-key (kbd "C-t") 'hydra-transpose/body)

Helper function to transform date to yyyy-mm-dd format:

(defun xah-fix-datetime (@begin @end)
  "Change timestamp under cursor into a yyyy-mm-dd format.
If there's a text selection, use that as input, else use current line.
Replace the text in selection or current line.

Any “day of week”, or “time” info, or any other parts of the string, are discarded.
For example:
 TUESDAY, FEB 15, 2011 05:16 ET → 2011-02-15
 November 28, 1994              → 1994-11-28
 Nov. 28, 1994                  → 1994-11-28
 11/28/1994                     → 1994-11-28
 1994/11/28                     → 1994-11-28

URL `http://ergoemacs.org/emacs/elisp_datetime_parser.html'
Version 2020-09-08"
  (interactive
   (list
    (if (region-active-p) (region-beginning))
    (if (region-active-p) (region-end))))
  (require 'parse-time)
  (let ($p1 $p2 $in)
    (if @begin
        (setq $p1 @begin $p2 @end)
      (setq $p1 (line-beginning-position) $p2 (line-end-position)))
    (setq $in (replace-regexp-in-string "^ *\\(.+\\) *$" "\\1" (buffer-substring-no-properties $p1 $p2)))
																				; remove white spaces

    (setq $in
          (cond

           ;; yyyy/mm/dd
           ((string-match "\\([0-9][0-9][0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)" $in)
            (concat (match-string 1 $in) "-" (match-string 2 $in) "-" (match-string 3 $in)))

           ;; mm/dd/yyyy
           ((string-match "\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-" (match-string 1 $in) "-" (match-string 2 $in)))
           ;; m/dd/yyyy
           ((string-match "\\([0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-0" (match-string 1 $in) "-" (match-string 2 $in)))

           ;; USA convention of mm/dd/yy
           ((string-match "\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-" (match-string 1 $in) "-" (match-string 2 $in)))
           ;; USA convention of m/dd/yy
           ((string-match "\\([0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-0" (match-string 1 $in) "-" (match-string 2 $in)))

					 ;; Inserted by Timm Lichte -->
           ;; dd.mm.yyyy
           ((string-match "\\([0-9][0-9]\\).\\([0-9][0-9]\\).\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-" (match-string 2 $in) "-" (match-string 1 $in)))
           ;; d.mm.yyyy
           ((string-match "\\([0-9]\\).\\([0-9][0-9]\\).\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-" (match-string 2 $in) "-0" (match-string 1 $in)))
					 ;; d.m.yyyy
           ((string-match "\\([0-9]\\).\\([0-9]\\).\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-0" (match-string 2 $in) "-0" (match-string 1 $in)))
					 ;; dd.m.yyyy
           ((string-match "\\([0-9][0-9]\\).\\([0-9]\\).\\([0-9][0-9][0-9][0-9]\\)" $in)
            (concat (match-string 3 $in) "-0" (match-string 2 $in) "-" (match-string 1 $in)))
           ;; dd.mm.yy
           ((string-match "\\([0-9][0-9]\\).\\([0-9][0-9]\\).\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-" (match-string 2 $in) "-" (match-string 1 $in)))
           ;; d.mm.yy
           ((string-match "\\([0-9]\\).\\([0-9][0-9]\\).\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-" (match-string 2 $in) "-0" (match-string 1 $in)))
					 ;; dd.m.yy
           ((string-match "\\([0-9][0-9]\\).\\([0-9]\\).\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-0" (match-string 2 $in) "-" (match-string 1 $in)))
					 ;; d.m.yy
           ((string-match "\\([0-9]\\).\\([0-9]\\).\\([0-9][0-9]\\)" $in)
            (concat (format-time-string "%C") (match-string 3 $in) "-0" (match-string 2 $in) "-0" (match-string 1 $in)))
					 ;; Inserted by Timm Lichte <--
					 
           ;; some ISO 8601. yyyy-mm-ddThh:mm
           ((string-match "\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)T[0-9][0-9]:[0-9][0-9]" $in)
            (concat (match-string 1 $in) "-" (match-string 2 $in) "-" (match-string 3 $in)))
           ;; some ISO 8601. yyyy-mm-dd
           ((string-match "\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)" $in)
            (concat (match-string 1 $in) "-" (match-string 2 $in) "-" (match-string 3 $in)))
           ;; some ISO 8601. yyyy-mm
           ((string-match "\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)" $in)
            (concat (match-string 1 $in) "-" (match-string 2 $in)))

           ;; else
           (t
            (progn
              (setq $in (replace-regexp-in-string "January " "Jan. " $in))
              (setq $in (replace-regexp-in-string "February " "Feb. " $in))
              (setq $in (replace-regexp-in-string "March " "Mar. " $in))
              (setq $in (replace-regexp-in-string "April " "Apr. " $in))
              (setq $in (replace-regexp-in-string "May " "May. " $in))
              (setq $in (replace-regexp-in-string "June " "Jun. " $in))
              (setq $in (replace-regexp-in-string "July " "Jul. " $in))
              (setq $in (replace-regexp-in-string "August " "Aug. " $in))
              (setq $in (replace-regexp-in-string "September " "Sep. " $in))
              (setq $in (replace-regexp-in-string "October " "Oct. " $in))
              (setq $in (replace-regexp-in-string "November " "Nov. " $in))
              (setq $in (replace-regexp-in-string "December " "Dec. " $in))

              (setq $in (replace-regexp-in-string "\\([0-9]+\\)st" "\\1" $in))
              (setq $in (replace-regexp-in-string "\\([0-9]+\\)nd" "\\1" $in))
              (setq $in (replace-regexp-in-string "\\([0-9]+\\)rd" "\\1" $in))
              (setq $in (replace-regexp-in-string "\\([0-9]\\)th" "\\1" $in))

              (let ($dateList $year $month $date $yyyy $mm $dd )
                (setq $dateList (parse-time-string $in))
                (setq $year (nth 5 $dateList))
                (setq $month (nth 4 $dateList))
                (setq $date (nth 3 $dateList))

                (setq $yyyy (number-to-string $year))
                (setq $mm (if $month (format "%02d" $month) "" ))
                (setq $dd (if $date (format "%02d" $date) "" ))
                (concat $yyyy "-" $mm "-" $dd))))))
    (delete-region $p1 $p2 )
    (insert $in)))

hydra-drag-stuff

For package drag-stuff.

(defhydra hydra-drag-stuff (:color red)
	"Drag stuff"
	("<left>" drag-stuff-left "left")
	("<right>" drag-stuff-right "right")
	("<up>" drag-stuff-up "up")
	("<down>" drag-stuff-down "down")
	("q" nil "cancel" :color blue))

hydra: end

		)                   

Key bindings

  • State “TODO” from [2023-10-02 Mon 22:43]
  • [ ] Overhaul and systematize key bindings.

tlkeys-mode

Opinionated Emacs key bindings just for this init file.

(define-minor-mode tlkeys-mode
	"Opinionated Emacs key bindings just for this init file."
	:lighter nil
	:global t
	:init-value t
	:keymap (let ((map (make-keymap)))

						(define-key input-decode-map (kbd "C-i") (kbd "H-i"))  ; to disentangle <tab> and C-i

						;; disentangle keys when using Emacs server
						;; local-function-key-map or input-decode-map are terminal-local
						(defun my-disentangled-keys (&optional frame)
							(with-selected-frame frame
								(define-key input-decode-map (kbd "C-i") (kbd "H-i"))  ; to disentangle <tab> and C-i
								))
						(add-hook 'after-make-frame-functions #'my-disentangled-keys)

						;; delete 
						(define-key map (kbd "C-d") nil)
						(define-key map (kbd "C-d C-k") 'kill-line)
						(define-key map (kbd "C-d <up>") #'(lambda () (interactive) (tl/kill-forward-line -1)))
						(define-key map (kbd "C-d <left>") 'delete-backward-char)
						(define-key map (kbd "C-d <right>") 'delete-forward-char)
						(define-key map (kbd "C-d <down>") #'(lambda () (interactive) (tl/kill-forward-line 1)))
						(define-key map (kbd "C-d C-<up>") #'(lambda () (interactive) (tl/kill-forward-line -1)))
						(define-key map (kbd "C-d C-<left>") 'delete-backward-char)
						(define-key map (kbd "C-d C-<right>") 'delete-forward-char)
						(define-key map (kbd "C-d C-<down>") #'(lambda () (interactive) (tl/kill-forward-line 1)))
						(define-key map (kbd "C-d C-o") 'delete-blank-lines)
						(define-key map (kbd "C-d C-m") 'remove-newlines-or-blank-lines-dwim)
						(define-key map (kbd "C-d C-<return>") 'remove-newlines-or-blank-lines-dwim)
						(define-key map (kbd "C-d C-a") #'(lambda () (interactive) (kill-line 0)))
						(define-key map (kbd "C-d C-e") 'kill-line)
						(define-key map (kbd "C-S-d") 'kill-whole-line)
						(define-key map (kbd "C-d SPC") 'tl/remove-extra-spaces-dwim)
						(define-key map (kbd "C-d C-SPC") 'tl/remove-extra-spaces-dwim)
						(define-key map (kbd "C-d TAB") 'tl/delete-indentation)
						(define-key map (kbd "C-d C-TAB") 'tl/delete-indentation)
						(define-key map (kbd "C-d [") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d ]") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d {") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d }") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d (") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d )") 'sp-unwrap-sexp)
						(define-key map (kbd "C-d DEL") 'hungry-delete-backward)
						(define-key map (kbd "C-d <deletechar>") 'hungry-delete-forward)

						;; LaTeX
						(define-key LaTeX-mode-map (kbd "C-d m") 'TeX-delete-macro)
						(define-key LaTeX-mode-map (kbd "C-d e") 'TeX-delete-environment)
						(define-key LaTeX-mode-map (kbd "C-d t") 'TeX-clean)

						;; return
						(define-key input-decode-map (kbd "C-m") (kbd "H-o")) ; to disentangle <return> and C-m
						(define-key key-translation-map (kbd "H-o") (kbd "RET"))
						(define-key map (kbd "C-S-m") 'smart-open-line)

						;; org-mode
						(define-key org-mode-map (kbd "C-d r") 'org-babel-remove-result)
						(define-key org-mode-map (kbd "C-d f") 'embrace-delete) ; delete emphasis/font
						(define-key org-mode-map (kbd "C-d (") 'embrace-delete)
						(define-key org-mode-map (kbd "C-d *") 'tl/embrace-delete-*)
						(define-key org-mode-map (kbd "C-d /") 'tl/embrace-delete-/)
						(define-key org-mode-map (kbd "C-d +") 'tl/embrace-delete-+)
						(define-key org-mode-map (kbd "C-d _") 'tl/embrace-delete-_)
						(define-key org-mode-map (kbd "C-d =") 'tl/embrace-delete-=)
						(define-key org-mode-map (kbd "C-d ~") 'tl/embrace-delete-~) 
						(define-key org-mode-map (kbd "C-c f") 'embrace-add)
						(define-key org-mode-map (kbd "C-d c") 'org-table-delete-column)
						(define-key org-mode-map (kbd "C-d C-c") 'org-table-delete-column)
						
						;; navigation
						(define-key map (kbd "C-j") 'hydra-jump/body)
						(define-key map (kbd "C-S-j") 'ace-jump-line-mode)
						(define-key map (kbd "H-i") #'(lambda ()
																					 (interactive)
																					 (if (string= (buffer-mode) "org-mode")
																							 (progn
																								 (org-goto)
																								 (recenter-top-bottom))
																						 (helm-imenu))))
						(define-key helm-map (kbd "H-i") 'helm-select-action) ; show actions (default is <tab>)
																				; (define-key map (kbd "C-S-i") 'imenu-list) ; disabled because it will break org-edit-special

						;; buffers
						(when (require 'bufler nil t)
							(define-key map (kbd "C-x b") 'bufler))
						(when (require 'helm-bufler nil t)
							(define-key map (kbd "C-x C-b") 'helm-bufler-go))
						(define-key map (kbd "C-x C-s") 'save-some-buffers)

						;; helm
						(define-key helm-map (kbd "C-n") 'helm-next-source)
						(define-key helm-map (kbd "C-p") 'helm-previous-source)
						(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) ; complete with <tab> (default is <ret>)
						(define-key helm-map (kbd "C-z") 'helm-select-action) ; show actions (default is <tab>)
						(define-key helm-map (kbd "<left>") 'backward-char) ; instead of C-f
						(define-key helm-map (kbd "<right>") 'forward-char) ; instead of C-b
						(define-key helm-map (kbd "M-<left>") 'previous-history-element)
						(define-key helm-map (kbd "M-<right>") 'next-history-element)

						map)
	;; body
	(add-hook 'minibuffer-setup-hook 'tlkeys-minibuffer)
	)

Key bindings in the minibuffer:

(defun tlkeys-minibuffer ()
	"Keymap for the minibuffer."
	(let ((map minibuffer-local-map))

		;; delete 
		(define-key map (kbd "C-d") nil)
		(define-key map (kbd "C-d C-k") 'kill-line)
		(define-key map (kbd "C-d <up>") #'(lambda () (interactive) (tl/kill-forward-line -1)))
		(define-key map (kbd "C-d <left>") 'delete-backward-char)
		(define-key map (kbd "C-d <right>") 'delete-forward-char)
		(define-key map (kbd "C-d <down>") #'(lambda () (interactive) (tl/kill-forward-line 1)))
		(define-key map (kbd "C-d C-<up>") #'(lambda () (interactive) (tl/kill-forward-line -1)))
		(define-key map (kbd "C-d C-<left>") 'delete-backward-char)
		(define-key map (kbd "C-d C-<right>") 'delete-forward-char)
		(define-key map (kbd "C-d C-<down>") #'(lambda () (interactive) (tl/kill-forward-line 1)))
		(define-key map (kbd "C-d C-a") #'(lambda () (interactive) (kill-line 0)))
		(define-key map (kbd "C-S-d") 'kill-whole-line)
		(define-key map (kbd "C-d [") 'sp-unwrap-sexp)
		(define-key map (kbd "C-d ]") 'sp-unwrap-sexp)
		(define-key map (kbd "C-d {") 'sp-unwrap-sexp)
		(define-key map (kbd "C-d }") 'sp-unwrap-sexp)
		(define-key map (kbd "C-d (") 'sp-unwrap-sexp)
		(define-key map (kbd "C-d )") 'sp-unwrap-sexp)
		))

Start mode:

;; (add-hook 'text-mode-hook 'tlkeys-mode) ; superseded by :init-value t
;; (add-hook 'prog-mode-hook 'tlkeys-mode) ; superseded by :init-value t

Helper functions:

(defun tl/kill-forward-line (N)
	(interactive)
	(save-excursion
		(when (= (forward-line N) 0)
			(kill-whole-line))))

underi-mode

Key bindings for more ergonomic cursor movement: the cursor movement keys are under the <i> key and get activated with the Meta/Alt key.

Inspired by: http://ergoemacs.org/emacs/emacs_useful_user_keybinding.html

(define-minor-mode underi-mode
	"Key bindings for more ergonomic cursor movement: the cursor movement keys are under the <i> key and get activated with the Meta/Alt key. 
	Inspired by: http://ergoemacs.org/emacs/emacs_useful_user_keybinding.html"
	:lighter " ui"
	:global t
	:init-value t
	:keymap (let ((map (make-keymap)))
						
						;; cursor keys
						(define-key key-translation-map (kbd "M-i") (kbd "<up>"))
						(define-key key-translation-map (kbd "M-k") (kbd "<down>"))
						(define-key key-translation-map (kbd "M-j") (kbd "<left>"))		
						(define-key key-translation-map (kbd "M-l") (kbd "<right>"))
						(define-key key-translation-map (kbd "M-I") (kbd "S-<up>"))
						(define-key key-translation-map (kbd "M-K") (kbd "S-<down>"))
						(define-key key-translation-map (kbd "M-J") (kbd "S-<left>"))		
						(define-key key-translation-map (kbd "M-L") (kbd "S-<right>"))

						;; more cursor keys
						(define-key map (kbd "M-n") 'scroll-up-command)
						(define-key map (kbd "M-p") 'scroll-down-command)
						(define-key map (kbd "C-M-n") #'(lambda () (interactive) (scroll-up 3)))
						(define-key map (kbd "C-M-p") #'(lambda () (interactive) (scroll-down 3)))
						(define-key map (kbd "M-o") 'point-redo)
						(define-key map (kbd "M-u") 'point-undo)
						(define-key map (kbd "M-z") 'goto-last-change)
						(define-key map (kbd "C-M-i") 'backward-paragraph)
						(define-key map (kbd "C-M-k") 'forward-paragraph)
						(define-key map (kbd "C-M-j") 'left-word)
						(define-key map (kbd "C-M-l") 'right-word)

						(define-key map (kbd "M-s-j")  'windmove-left)
						(define-key map (kbd "M-s-l") 'windmove-right)
						(define-key map (kbd "M-s-i")   'windmove-up)
						(define-key map (kbd "M-s-k")  'windmove-down)

						map)
	;; body
	(add-hook 'minibuffer-setup-hook 'underi-minibuffer)
	)	

Key bindings in the minibuffer:

(defun underi-minibuffer ()
	"Keymap for the minibuffer."
	(let ((map minibuffer-local-map))

		;; move cursor
		(define-key map (kbd "M-n") 'scroll-up-command)
		(define-key map (kbd "M-p") 'scroll-down-command)
		(define-key map (kbd "M-o") 'point-redo)
		(define-key map (kbd "M-u") 'point-undo)
		(define-key map (kbd "M-z") 'goto-last-change)
		(define-key map (kbd "C-M-i") 'backward-paragraph)
		(define-key map (kbd "C-M-k") 'forward-paragraph)
		(define-key map (kbd "C-M-j") 'left-word)
		(define-key map (kbd "C-M-l") 'right-word)
		
		))

Start mode:

	;; (add-hook 'text-mode-hook 'underi-mode) ; superseded by :init-value t
	;; (add-hook 'prog-mode-hook 'underi-mode) ; superseded by :init-value t

winkeys-mode

Key bindings roughly following the conventions of the Windows habitat.

(define-minor-mode winkeys-mode
	"Key bindings roughly following the conventions of the Windows habitat."
	:init-value t
	:global t
	:lighter " wk"
	:keymap (let ((map (make-keymap)))
						
						;; save
						(define-key map (kbd "C-s") 'save-buffer)
						(define-key map (kbd "C-S-s") 'write-file)

						;; search and replace
						(define-key map (kbd "C-f") #'(lambda () 
																						(interactive)
																						(if (string= (buffer-mode) "pdf-view-mode")
																								;; helm-swoop does not work here
																								(call-interactively 'pdf-occur) ; `call-interactively' is needed by occur
																							(helm-swoop))))
						;; (define-key map (kbd "C-S-f a") 'helm-multi-swoop-all)
						;; (define-key map (kbd "C-S-f m") 'helm-multi-swoop-current-mode)
						;; (define-key map (kbd "C-f") 'swiper-helm)
						;; (define-key map (kbd "C-f") 'swiper)
						;; (define-key map (kbd "C-f") 'isearch-search)
						;; (define-key map (kbd "C-S-f s") 'isearch-forward)
						;; (define-key map (kbd "C-S-f C-s") 'isearch-forward)
						;; (define-key map (kbd "C-S-f r") 'isearch-backward)
						;; (define-key map (kbd "C-S-f C-r") 'isearch-backward)
						(define-key map (kbd "C-r") 'vr/replace)
						(define-key map (kbd "C-S-r") 'vr/query-replace)
						(define-key map (kbd "C-o") 'helm-find-files)

						;; mark all
						(define-key map (kbd "C-x C-a") 'mark-whole-buffer)
						
						;; quit
						(define-key key-translation-map (kbd "M-q") (kbd "C-g"))

						;; undo/redo
						;; (define-key map (kbd "C-z") 'undo-tree-undo)
						;; (define-key map (kbd "C-S-z") 'undo-tree-redo)

						;; yank
						(define-key map (kbd "C-v") 'yank)						

						;; press ESC only once
						(define-key map (kbd "<escape>") 'keyboard-escape-quit)
						
						map
						)

	;; body 

	(add-hook 'minibuffer-setup-hook 'winkeys-minibuffer)

	(with-eval-after-load 'helm-swoop
		(define-key helm-swoop-map (kbd "C-r") 'helm-previous-line)
		(define-key helm-swoop-map (kbd "C-f") 'tl/helm-swoop-C-s) 
		(define-key helm-multi-swoop-map (kbd "C-r") 'helm-previous-line)
		(define-key helm-multi-swoop-map (kbd "C-f") 'helm-next-line)
		(define-key helm-swoop-map (kbd "C-S-f a") 'helm-multi-swoop-all-from-helm-swoop)
		(define-key helm-swoop-map (kbd "C-S-f m") 'helm-multi-swoop-current-mode-from-helm-swoop)
		(define-key helm-swoop-map (kbd "M-a") 'helm-multi-swoop-all-from-helm-swoop))

	(with-eval-after-load 'swiper-helm
		(define-key swiper-helm-keymap (kbd "C-r") 'helm-previous-line)
		(define-key swiper-helm-keymap (kbd "C-f") 'tl/swiper-helm-C-s))

	(with-eval-after-load 'company
		(define-key company-active-map (kbd "C-s") 'save-buffer))

	;; (define-key key-translation-map (kbd "C-v") (kbd "C-y")) ; unfortunately this interferes with key chords using C-v
	)

Key bindings in the minibuffer:

(defun winkeys-minibuffer ()
	"Keymap for the minibuffer."
	(let ((map minibuffer-local-map))

		;; undo/redo
		(define-key map (kbd "C-z") 'undo-tree-undo)
		(define-key map (kbd "C-S-z") 'undo-tree-redo)
		
		))

Let helm-swoop behave more like isearch:

	;; FIXME: does not follow search hits; helm-action-follow-forward does not work here
	(defun tl/helm-swoop-C-s ()
		(interactive)
		(if (boundp 'helm-swoop-pattern)
				(if (equal helm-swoop-pattern "")
						(previous-history-element 1)
					(helm-next-line))
			(helm-next-line)
			))

TESTING: Let swiper-helm behave more like isearch:

	(defun tl/swiper-helm-C-s ()
		(interactive)
		(if (boundp 'helm-pattern)
				(if (equal helm-pattern "")
						(previous-history-element 1)
					(helm-next-line))
			(helm-next-line)
			))

winkeys-starter-mode

https://github.com/publicus/emacs-org-mode-for-the-laity

Debugging

Startup

–debug-init

emacs --debug-init

pkill

It has proven surprisingly useful to debug problems within Emacs from outside, namely by invoking pkill from a shell like this:

pkill -SIGUSR2 emacs

I learned about this way of debugging Emacs from a question on StackOverflow: https://stackoverflow.com/q/65815662/6452961

Delete org-persist files

If you experience strange crashes when starting emacs, it has proven effective to delete the content of the folder ~/.cache/org-persist. Don’t ask me why.

org-persist is used to store cached data across Emacs sessions.

Functions

debug-on-entry

https://www.gnu.org/software/emacs/manual/html_node/eintr/debug_002don_002dentry.html

(debug-on-entry FUNCTION)

Request FUNCTION to invoke debugger each time it is called. Use M-x cancel-debug-on-entry to cancel the effect of this command.

toggle-debug-on-error

Toggle whether to enter Lisp debugger when an error is signaled.

Org

https://orgmode.org/manual/Feedback.html#Feedback

Private settings

Load file with private settings after generic settings.

(let ((private-settings-file (expand-file-name "private-emacs-settings-after.el" private-emacs-settings-dir)))
	(when (file-exists-p private-settings-file)
		(load-file private-settings-file)))