155 KiB
emacs init file
- Tasks that need attention
- files saved with mu4e have restrictive permissions.
- Mouse still shows tooltips in mu4e:view mode
- Find something to put the C-z prefix to good use
- org-babel open-line creates two newlines.
- BUG!!!!!!!!!!!! invalid target location, and the capture is gone.
- C-a does not work as expected when the subtree is expanded
- migrate from patching org-mode to a more sustainable option
- can we run a task with an org-link?
- improve helm-mu-contacs
- remove duplicates in contacts list with double spaces and such
- take care of the "dear To" when completing the name in an email
- make an alist for names to nicknames when completing the name…
- how to "open with" attachments with mu4e?
- investigate what is wrong with inline-pdf image width
- fix the issue with highlighting bleeding over the margin for
- The window management is sometimes frustrating
- Autosort token for some subtrees
- Work on a "welcome" read-only org page for each activity in
- revert org-files on frame activation.
- explore this possibility about org-mode paper managing
- Features to explore
- Look at tangling at
- About org-mode
- Introduction
- Files and dirs
- Themes
- Global settings
- Global helper functions
- Cosmetics
- Mouseless
- Window behavior
- Scrolling
- Mode-line
- Coding system
- Mark handling
- Global bindings
- Disable commands
- Smart beginning-of-line
- Smart open-line
- Whitespace
- Show matching parens
- Load hydra
- Abbrevs
- Spellcheck
- Fringe treatment
- Turn on echo mode
- frame helpers
- Disable completion buffer
- Auto save on compile
- Idle-kill
- Calendar
- Use local proxy
- Indicator
- COMMENT Tarmak-ready
- Transmission
- Tree-sitter grammars
- KDE integration
- Main major modes
- mu4e
- org-mode
- Require
- Hooks
- Helper for quotes
- Cosmetics
- Ligatures [not in working order]
- Links
- Agenda
- Automate saving
- cycling-headlines
- Reverting stuff from orgzly
- Hydras for timestamps
- Hydras for refiling
- Hydras for moving headlines around
- tags-to-filename
- Capture
- LaTeX export
- Source blocks
- Lowercase org blocks
- Clocking
- COMMENT Tangle to different files
- Append tangle
- Tangle file
- Export file
- Crypto stuff
- ox-export
org-roamsetup- export to ics
- org-babel
- Make reftex work in org-edit
- elisp
- qml
- Python
- Dired
- pdf-tools
- C and C++
- split — LaTeX
- Journal
- Specialties
- Main packages
- Tidy-up
- Moving to colemak
- Finale
TODO Tasks that need attention
TODO files saved with mu4e have restrictive permissions.
TODO Mouse still shows tooltips in mu4e:view mode
TODO Find something to put the C-z prefix to good use
TODO org-babel open-line creates two newlines.
TODO BUG!!!!!!!!!!!! invalid target location, and the capture is gone.
TODO C-a does not work as expected when the subtree is expanded
TODO migrate from patching org-mode to a more sustainable option
the issue is that I wanted to make sure that nothing would break by managing conflicts by hand, but this is not happening in practice
DONE can we run a task with an org-link?
For instance, I want to call kmail in "zero inbox proton" i can run elisp code…
DONE improve helm-mu-contacs
- Manage ignore list We can advise ~ helm-mu-contacts-transformer~ and remove the candidates
- Implement action for ignoring contacts
TODO remove duplicates in contacts list with double spaces and such
DONE take care of the "dear To" when completing the name in an email
DONE make an alist for names to nicknames when completing the name…
TODO how to "open with" attachments with mu4e?
DONE investigate what is wrong with inline-pdf image width
I need to cycle the mode on/off to make it work as intended
TODO fix the issue with highlighting bleeding over the margin for
ace-window
TODO The window management is sometimes frustrating
TODO Autosort token for some subtrees
TODO Work on a "welcome" read-only org page for each activity in
which we put links to what should be done in that activity. For instance
TODO revert org-files on frame activation.
What was the deal with auto-revert-mode again?
TODO explore this possibility about org-mode paper managing
TODO Features to explore
Study how to implement no-scroll mode
TODO Look at tangling at
[http://thewanderingcoder.com/2015/02/literate-emacs-configuration/]
although I'd rather do something like a local variable evaluation such as
Or makefile approach [https://emacs.stackexchange.com/questions/27126/is-it-possible-to-org-bable-tangle-an-org-file-from-the-command-line]
See also [http://orgmode.org/manual/Extracting-source-code.html#Extracting-source-code]
About org-mode
I am insisting to use my own branch of org-mode; in order to have
this work, we need to run the following code before
anything else. Add it to early-init.el. This file is tangled
every time emacs starts, so that's fine.
; this is required for using my branch of org-mode
(add-to-list 'load-path "~/clones/org-mode/lisp")
(require 'org-loaddefs)
Introduction
setq vs setq-default
Some variables are local, others are not; for non buffer-local variables the two commands are equivalent; for buffer-local variables, setq acts on the current buffer, whereas setq-default sets the default value of the variable for buffers that do not have a local value.
:source: [https://stackoverflow.com/a/18173666]
Why not package.el
it is convenient, but it is not git-based; borg.el is promising, but it is
way complicated; I can do everything by hand as soon as it is neatly
org-anized. In the end we use a few, but I plan to phase them out and
replace them with git submodules
Files and dirs
Move Customize to a separate file
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file 'noerror)
Add relevant dirs to load path [This is somewhat undesirable; I should uniformize the path-loading business at some point…]
(let ((default-directory "~/.emacs.d/"))
(normal-top-level-add-subdirs-to-load-path)
(normal-top-level-add-to-load-path '("biblio.el"
"fringe-helper.el"
"multiple-cursors.el"
"expand-region.el"
"dash.el"
"webpaste.el"
"highlight-parentheses.el")))
Put autosave files (ie #foo#) and backup files (ie foo~) in ~/.emacs.d/. create the autosave dir if necessary, since emacs won't.
(dolist (dir '("~/.emacs.d/autosaves/" "~/.emacs.d/backup/"))
(make-directory dir t))
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/autosaves/\\1" t))
backup-directory-alist '((".*" . "~/.emacs.d/backup/")))
Themes
Use (my clone of) the solarized theme
(add-to-list 'custom-theme-load-path "/home/jacopods/.emacs.d/emacs-color-theme-solarized")
(load-theme 'lunarized t)
Use patched terminus font (this overrides the settings in Customize, but I never could make it work otherwise). See the special treatment later on for dealing with bold weight. To avoid flickering one should also set the font in .Xresources (on XWayland, of course)
(set-face-font 'default "Hackminus")
(set-face-font 'fixed-pitch "Hackminus")
(set-face-font 'fixed-pitch-serif "Hackminus")
(set-face-font 'variable-pitch "Hackminus")
Global settings
Global helper functions
This is a helper function used to set several global keys given in
provided as a parameter in binding-alist
(defun global-set-key-alist (binding-alist)
"This function iterates over the binding-alist, which should be an alist of key and binding and sets each binding in the global-key-map"
(dolist (binding binding-alist)
(global-set-key (kbd (car binding)) (cdr binding))))
(defun define-key-alist (keymap binding-alist)
"This function iterates over the binding-alist, which should be an alist of key and binding and sets each binding in the global-key-map"
(dolist (binding binding-alist)
(define-key keymap (kbd (car binding)) (cdr binding))))
This is a hack to remove slant and bold from all faces ((why interactive?))
(defun wilder/fixup-faces ()
(interactive)
(dolist (face (face-list))
(set-face-attribute face nil :weight 'normal)
(set-face-attribute face nil :slant 'normal))
(set-face-font 'default "Hackminus"))
This function saves the current buffer after the specified amount of idle time, unless something calls it again and then the timer restarts
(defun save--buffer-buf (buf)
(with-current-buffer buf (save-buffer)))
(defun save-buffer-with-timer (secs)
(when (and (boundp 'save-timer) save-timer)
(cancel-timer save-timer))
(setq-local save-timer (run-with-idle-timer secs nil
#'save--buffer-buf (current-buffer))))
Message-and-wait
(defun debug-message (s)
(message s)
(sleep-for 0 75))
Cosmetics
Prefer a minimal appearance: no menu, toolbar or scroll-bars; no splash screens or messages
(menu-bar-mode -1)
(tool-bar-mode 0)
(scroll-bar-mode -1)
(setq inhibit-startup-screen t
inhibit-startup-message t)
A blinking cursor keeps the gpu awake; add global hl-line mode to more easily spot the cursor
(blink-cursor-mode 0)
(set-default 'cursor-type 'box)
Set frame title
(setq frame-title-format
'((buffer-file-name "%f"
(dired-directory dired-directory "%b")) " · emacs"))
Silence beeping
(setq ring-bell-function 'ignore)
Themed tooltips
(setq x-gtk-use-system-tooltips nil)
Set fill-column to 72, which happens to be good for third-tiled frames
and it also is a reasonable standard
(setq default-fill-column 72)
Use large margins (3 chars wide) and hardcode the width of the fringe to match the char width
(setq-default left-margin-width 3
right-margin-width 3
helm-left-margin-width left-margin-width
helm-buffers-left-margin-width left-margin-width)
(fringe-mode 10)
Setup page-break-lines
(require 'page-break-lines)
(setq page-break-lines-char ?·
page-break-lines-lighter "")
(add-to-list 'page-break-lines-modes 'latex-mode)
(add-to-list 'page-break-lines-modes 'TeX-latex-mode)
(add-to-list 'page-break-lines-modes 'tex-mode)
(global-page-break-lines-mode)
Mouseless
Disable mouse interaction with emacs
(mouse-wheel-mode -1)
(setq mouse-autoselect-window nil
mouse-yank-at-point nil
focus-follows-mouse nil
mouse-highlight nil)
TODO Find out what of the above is necessary given the disable-mouse mode below
Use the disable-mouse package by Steve Purcell available at
[https://github.com/purcell/disable-mouse]
(require 'disable-mouse)
(global-disable-mouse-mode)
Window behavior
Prevent compilation window eating up other windows
(setq compilation-window-height 12
compilation-scroll-output nil)
Prevent any automatic splitting to split vertically
(setq split-height-threshold 0)
Display-buffer rules
Make sure that manual buffer switch also obey display actions
(setq switch-to-buffer-obey-display-actions t)
;; TODO the docstring needs to be updated
(defun display-buffer-reuse-maybe-some-frame (buffer alist)
"Display BUFFER in an existing frame that meets a predicate.
The default predicate is to use any frame other than the selected
frame. If successful, return the window used; otherwise return
nil.
ALIST is an association list of action symbols and values. See
Info node `(elisp) Buffer Display Action Alists' for details of
such alists.
If ALIST has a non-nil `inhibit-switch-frame' entry, avoid
raising the frame. If it has a non-nil `frame-predicate' entry,
its value is a function taking one argument (a frame), returning
non-nil if the frame is a candidate; this function replaces the
default predicate. If ALIST has a non-nil `inhibit-same-window'
entry, avoid using the currently selected window (only useful
with a frame-predicate that allows using the selected frame).
This is an action function for buffer display, see Info
node `(elisp) Buffer Display Action Functions'. It should be
called only by `display-buffer' or a function directly or
indirectly called by the latter."
(let* ((predicate
(or (cdr (assq 'frame-predicate alist))
(lambda (frame)
(and (not (eq frame (selected-frame)))
(get-lru-window frame)))))
(frames (-filter predicate (frame-list-z-order)))
;; now frames contain all frame candidates in z-order
(frame (car frames))
(window
(and frame
(or
(car (get-buffer-window-list buffer 'nomini frame))
(get-lru-window
frame nil (cdr (assq 'inhibit-same-window alist)))))))
(when window
(prog1
(window--display-buffer buffer window 'reuse alist)
(unless (cdr (assq 'inhibit-switch-frame alist))
(window--maybe-raise-frame frame))))))
(defun frame-good-for-editing-p (frame)
(not (--any (eq 'pdf-view-mode it)
(--map (with-current-buffer it major-mode)
(-map #'window-buffer (window-list frame))))))
;(pop display-buffer-alist)
(add-to-list 'display-buffer-alist
'(".*.tex"
display-buffer-reuse-maybe-some-frame
(frame-predicate . frame-good-for-editing-p)))
First, treat mu4e-headers properly
(add-to-list 'display-buffer-alist
'("\\*mu4e-headers\\*"
display-buffer-reuse-window))
(add-to-list 'display-buffer-alist
'("\\*mu4e-main\\*"
display-buffer-reuse-window
(dedicated . t)
(inhibit-same-window . nil)))
Then, treat the calendar
(add-to-list 'display-buffer-alist
'("\\*Calendar\\*"
(display-buffer-in-side-window)
(side . bottom )
(slot . 0)
(window-height . 10)))
(add-to-list 'display-buffer-alist
'("\\*Outline .*pdf\\*"
(display-buffer-in-side-window)
(side . bottom )
(slot . 0)
(window-height . 29)))
Scrolling
Scrolling setup
(setq redisplay-dont-pause t
scroll-margin 3
scroll-step 1
scroll-conservatively 10000
scroll-preserve-screen-position 1)
TODO find out what this was actually meant to do
(setq-default display-buffer-reuse-frames t)
Mode-line
This snippet is taken from here (occasionallycogent). It serves the purpose of keeping track of the currently selected window, so that we can correctly highlight it in the modeline
(defvar cogent-line-selected-window (frame-selected-window))
(defun cogent-line-set-selected-window (&rest _args)
(when (not (minibuffer-window-active-p (frame-selected-window)))
(setq cogent-line-selected-window (frame-selected-window))
(force-mode-line-update)))
(defun cogent-line-unset-selected-window ()
(setq cogent-line-selected-window nil)
(force-mode-line-update))
(add-hook 'window-configuration-change-hook #'cogent-line-set-selected-window)
(add-hook 'focus-in-hook #'cogent-line-set-selected-window)
(add-hook 'focus-out-hook #'cogent-line-unset-selected-window)
(advice-add 'handle-switch-frame :after #'cogent-line-set-selected-window)
(advice-add 'select-window :after #'cogent-line-set-selected-window)
(defun cogent-line-selected-window-active-p ()
(eq cogent-line-selected-window (selected-window)))
Set a few parts of the mode-line
(setq-default mode-line-modified '(:eval (if (buffer-modified-p) "●" "·"))
;Still needs some improvements, does not report Readonly state
mode-line-remote '(:eval (let ((s (format-mode-line "%@")))
(cond
((equal s "-") "·") ((equal s "@") "@") (t s)))))
(defface mode-line-indicator
'((t nil))
"mode-line active indicator"
:group 'mode-line-faces)
(defface mode-line-inactive-indicator
'((t nil))
"Inactive variant of indicator"
:group 'mode-line-faces)
(defvar wilder-buffer-vc-mode-line
'("%b" (vc-mode (:propertize
;; Strip the backend name from the VC status information
(:eval (let* ((backend (downcase (symbol-name (vc-backend (buffer-file-name)))))
(branch (substring vc-mode (+ (length backend) 2)))
(s (substring vc-mode (+ (length backend) 1) (+ (length backend) 2)))
(status (cond ((equal s "-") "") ((equal s ":") "!") (t s))))
(concat "·" branch status)))
face magit-branch-local))))
(defvar wilder-position
'("%p · %I" ))
(put 'wilder-buffer-vc-mode-line 'risky-local-variable t)
(put 'wilder-position 'risky-local-variable t)
(defvar wilder/mode-line-modes
(list "%["
'("" mode-name)
'("" mode-line-process)
" "
'("" minor-mode-alist)
"%n"
"%]"
" ")
"Mode line construct for displaying major and minor modes.")
(put 'wilder/mode-line-modes 'risky-local-variable t)
(setq-default header-line-format nil);'("%e" ));wilder-buffer-vc-mode-line))
(setq mode-line-separator
'(:eval (let* ((len-left (length (format-mode-line mode-line-format-left)))
(len-right (length (format-mode-line mode-line-format-right)))
(len-separator (- (+ (window-width) 2 2 2) (+ len-left len-right))))
(format (format "%%%ds" len-separator) ""))))
(setq grab-handle (concat ""(with-temp-buffer
(insert-image (create-image
"P1 30 10
000000000000011110000000000000
000000000000011110000000000000
000000111100011110001111000000
000000111100011110001111000000
000000111100011110001111000000
000000111100011110001111000000
000000000000011110000000000000
000000000000011110000000000000
" 'pbm t :ascent 100) "---")
(buffer-string)))
grab-handle-unmodified (concat ""(with-temp-buffer
(insert-image (create-image
"P1 30 8
000000111100011110001111000000
000000111100011110001111000000
000000111100011110001111000000
000000111100011110001111000000
" 'pbm t :ascent 100) "---")
(buffer-string))))
(setq mode-line-format-left
'("%e"
(:eval (propertize (if (buffer-modified-p) grab-handle
grab-handle-unmodified)
'face (if (cogent-line-selected-window-active-p) 'mode-line-indicator 'mode-line-inactive-indicator)))
mode-line-front-space
mode-line-remote
mode-line-frame-identification
wilder-buffer-vc-mode-line
" "
wilder-position))
(setq mode-line-format-right
'(" "
wilder/mode-line-modes
mode-line-misc-info
" "))
(put 'mode-line-format-left 'risky-local-variable t)
(put 'mode-line-format-right 'risky-local-variable t)
(put 'mode-line-separator 'risky-local-variable t)
(setq-default mode-line-format
'("%e" mode-line-format-left
mode-line-separator
mode-line-format-right))
The following has been found in here to clean up the modeline I modified it marginally to avoid using the loop monster
(defvar mode-line-cleaner-alist
'()
"Alist for `clean-mode-line.'
When you add a new element to the alist, keep in mind that you
must pass the correct minor/major mode symbol and a string you
want to use in the modeline *in lieu of* the original.")
(setq mode-line-cleaner-alist
`((auto-complete-mode . " α")
(yas/minor-mode . " υ")
(paredit-mode . " π")
(eldoc-mode . "")
(abbrev-mode . "")
(auto-fill-function . "")
(disable-mouse-global-mode . "")
(auto-revert-mode . "")
(helm-mode . " η" )
(smart-tab-mode . "")
(subword-mode . "")
(outshine-mode . " o")
(outline-minor-mode . "")
(reftex-mode . "")
(flyspell-mode . "")
(geiser-mode . " γ")
(geiser-autodoc-mode . "·α")
(org-src-mode . " ωσ")
;; Major modes
(lisp-interaction-mode . "λ")
(hi-lock-mode . "")
(python-mode . "Py")
(emacs-lisp-mode . "ελ")
(help-mode . "+")
(scheme-mode . "λ")
(tex-mode . "χ")
(latex-mode . "χ")
(TeX-latex-mode . "χ")
(org-mode . "Ω")
(org-agenda-mode . "Ω:Agenda")))
(defun clean-mode-line ()
(interactive)
(dolist (cleaner mode-line-cleaner-alist)
(let* ((mode (car cleaner))
(mode-str (cdr cleaner))
(old-mode-str (cdr (assq mode minor-mode-alist))))
(when old-mode-str
(setcar old-mode-str mode-str))
;; major mode
(when (eq mode major-mode)
(setq mode-name mode-str)))))
(add-hook 'after-change-major-mode-hook 'clean-mode-line)
Coding system
Prefer the utf-8 coding system globally. This helps with the issue of magit not correctly staging hunks containing utf-8 characters. See [https://github.com/magit/magit/issues/32]
(prefer-coding-system 'utf-8)
Mark handling
No transient mark is more flexible
(transient-mark-mode 0)
But of course we need to see the mark
(require 'visible-mark)
(visible-mark-mode t)
(global-visible-mark-mode t)
The following are some convenient bindings; on my layout F17 and F18 are obtained by tapping the left and right shift respectively
(defun jump-to-mark ()
"Jumps to the local mark, respecting the `mark-ring' order.
This is the same as using \\[set-mark-command] with the prefix argument."
(interactive)
(set-mark-command 1))
(defun just-activate-mark ()
(interactive)
(activate-mark))
(global-set-key-alist
'(("<f17>" . set-mark-command)
("<f18>" . set-mark-command)
("<M-f17>" . jump-to-mark)
("<M-f18>" . jump-to-mark)
("<S-f17>" . just-activate-mark)
("<S-f18>" . just-activate-mark)))
;; prevent the menu-bar from stealing my binding
(advice-add 'menu-bar-enable-clipboard :override (lambda ()))
See here for more information about the last line
Global bindings
Remove some bindings that I find supremely annoying.
Additionally, unbind the C-x o binding to force me using ace-window
Finally, unbind the C-SPC to mark the point, as I am using LR-shift
Note that I will use C-SPC as a hydra below to deal with parenthesis
(dolist (ch '("C-h C-n" "C-c C-x" "C-x C-r" "C-z" "C-x f" "C-x C-f" "<M-f4>" "C-M-u" "C-M-d" "C-x o" "C-SPC"))
(global-unset-key (kbd ch)))
Change {up,down}-list
(global-set-key-alist '(( "C-M-i" . down-list)
( "C-M-o" . up-list)))
Add some opinionated bindings
(global-set-key-alist
'(("C-M-d" . kill-sexp)
("C-M-<backspace>" . backward-kill-sexp)
("C-x k" . kill-current-buffer)
("C-S-v" . scroll-down-command)
("C-M-u" . universal-argument)))
Disable commands
Enable narrow commands
(dolist (fun '(narrow-to-region
LaTeX-narrow-to-environment
narrow-to-page))
(put fun 'disabled nil))
Smart beginning-of-line
This is a relatively smart way to go back to the beginning of the line
(defun smart-line-beginning ()
"Move point to the beginning of text on the current line; if that
is already the current position of point, then move it to the
beginning of the line."
(interactive)
(let ((pt (point)))
(beginning-of-line-text)
(when (eq pt (point))
(beginning-of-line))))
(global-set-key (kbd "C-a") 'smart-line-beginning)
Smart open-line
(defun open-line-and-indent (arg)
(interactive "*p")
(save-excursion
(newline-and-indent)
(skip-chars-forward "[:blank:]")
(when (eolp)
(let ((e (point))
(b (progn (beginning-of-line) (point))))
(delete-region b e)))))
(global-set-key (kbd "C-o") #'open-line-and-indent)
Whitespace
Trailing whitespace is evil; get rid of it unless we are in special modes, namely calendar (and later mu4e)
(setq-default show-trailing-whitespace t)
(add-hook 'calendar-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
(add-hook 'mu4e-main-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
(add-hook 'helm-major-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
(add-hook 'before-save-hook 'delete-trailing-whitespace)
Define what is whitespace during a search
(setq search-whitespace-regexp "[ \t\r\n]+")
Tabs are evil; get rid of them
(setq c-basic-offset 4)
(setq-default tab-width 4
indent-tabs-mode nil)
Show matching parens
Use show-paren for paren-highlighting
(show-paren-mode 1)
Load hydra
(require 'hydra)
Abbrevs
Set up abbrevs
(setq-default abbrev-mode t)
(read-abbrev-file "~/.abbrev_defs")
(setq save-abbrevs t)
Spellcheck
Use Canadian spelling
(setq ispell-dictionary "canadian"
ispell-local-dictionary "canadian"
ispell-local-dictionary-alist
'(("canadian" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "canadian") nil utf-8)))
Use flyspell
(require 'flyspell-lazy)
(flyspell-lazy-mode 1)
(add-hook 'text-mode-hook 'flyspell-mode)
(setq flyspell-use-meta-tab nil)
(defface flyspell-margin-incorrect
'((t nil))
"flyspell-margin-incorrect"
:group 'flyspell)
(setq flyspell-before-incorrect-word-string
(propertize "." 'display `((margin left-margin)
,(propertize "×" 'face 'flyspell-margin-incorrect))))
Fringe treatment
Add margin marker for current line
Add marker for current line in the margin (see [https://github.com/kyanagi/fringe-current-line] and [https://github.com/nschum/fringe-helper.el] for inspiration)
(require 'margin-current-line)
(global-margin-current-line-mode 1)
Turn on echo mode
Display unfinished commands in the echo area after the specified delay.
(setq echo-keystrokes 0.10)
frame helpers
(defmacro with-new-frame (&rest body)
`(with-selected-frame (make-frame) ,@body))
(defmacro eval-with-new-frame (&rest body)
`(lambda () (interactive) (with-new-frame ,@body)))
Disable completion buffer
Remove the completion buffer as soon as we get out of the minibuffer
(add-hook 'minibuffer-exit-hook
'(lambda ()
(let ((buffer "*Completions*"))
(and (get-buffer buffer)
(kill-buffer buffer))) ))
Auto save on compile
(setq compilation-ask-about-save nil)
Idle-kill
(defun kill-buffers-on-idle ()
"Kill buffers that have the appropriate property set."
(save-excursion
(save-window-excursion
(dolist (b (buffer-list))
(when (and (with-current-buffer b (boundp 'buffer-close-on-idle))
(with-current-buffer b buffer-close-on-idle))
(with-current-buffer b (message "Killed %s" buffer-file-name))
(pop-to-buffer-same-window b)
(kill-buffer))))))
(run-with-idle-timer 180 t 'kill-buffers-on-idle)
Calendar
(add-hook 'calendar-load-hook
(lambda ()
(calendar-set-date-style 'european)))
;; first day of the week is monday instead of sunday:
(setq calendar-week-start-day 1
calendar-location-name "TO"
calendar-latitude 43.7
calendar-longitude -79.4
calendar-today-marker "·")
(add-hook 'today-visible-calendar-hook 'calendar-mark-today)
Use local proxy
I need this to route bibretrieve requests through my local proxy,
that in turn routes requests to mathscinet through the University network
;; Use system proxy
(setq url-proxy-services '(("http" . "127.0.0.1:8118")
("https" . "127.0.0.1:8118")))
Indicator
Change cursor color to reflect different layers on the keyboard This is the handler
(defun xkbvleds-indicator-signal-handler (indicator on)
(if on
(cond ((string-equal indicator "Greek")
(set-face-background 'cursor "#2aa198"))
((string-equal indicator "Math")
(set-face-background 'cursor "#cb4b16")))
(set-face-background 'cursor "#919191")))
Connect the signal from xkbvleds to the handler
(require 'dbus)
;; Register for the signal indicatorChanged coming from our service. It is
;; important that the =service= argument is =nil=, otherwise the registration
;; would stop once the process is respawned.
(ignore-errors (dbus-register-signal
:session nil "/org/xkbvleds" "org.xkbvleds" "indicatorChanged"
'xkbvleds-indicator-signal-handler))
COMMENT Tarmak-ready
Set up all possible combinations of modifiers
(defun concat-recursive (b &optional a)
(if b
(append (concat-recursive (cdr b) (concat a (car b)))
(concat-recursive (cdr b) a))
(when a (list a))))
(setq modifier-combo (concat-recursive '("C-" "M-" "s-" "H-")))
Then list the swapped keys
(setq tarmak1-swaps '(("j" . "e")
("e" . "k")
("k" . "n")
("n" . "j")))
Transmission
(require 'transmission)
(setq transmission-host "192.168.0.40")
Tree-sitter grammars
(use-package treesit-auto
:config
(global-treesit-auto-mode))
(setq treesit-auto-install 'prompt)
TODO KDE integration
TODO cleanup and split
These functions connects to dbus to find out the current activity id and name
(defun kde-current-activity ()
"Returns the current KDE activity"
(substring (let ((default-directory "~")) (shell-command-to-string "qdbus6 org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity")) 0 -1))
(defun kde-current-activity-name ()
"Returns the name of the current KDE activity"
(substring (let ((default-directory "~")) (shell-command-to-string (concat "qdbus6 org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.ActivityName " (kde-current-activity)))) 0 -1))
(defun X-window-id-belongs-to-activity (window-id activity)
(= (shell-command (concat "xprop -id " window-id " | grep _KDE_NET_WM_ACTIVITIES | grep " activity ">/dev/null")) 0)
)
(defun select-frame-on-activity (activity)
(setq framelist (frame-list))
(setq done nil)
(while (and framelist (not done))
(setq cur (car framelist))
(setq cur-id (cdr (assq 'window-id (frame-parameters cur))))
(if cur-id (if (X-window-id-belongs-to-activity cur-id activity) (progn;
(setq done 't) (select-frame cur)) ))
(setq framelist (cdr framelist)))
done
)
(defun select-X-frame ()
(setq framelist (frame-list))
(setq done nil)
(while (and framelist (not done))
(setq cur (car framelist))
(setq cur-id (cdr (assq 'window-id (frame-parameters cur))))
(if cur-id (progn;
(setq done 't) (raise-frame cur)))
(setq framelist (cdr framelist)))
done
)
This function is used to raise the frame associated to the current activity
TODO These entries should be added to the subtree once it is split
(defun select-frame-on-current-activity ()
(select-frame-on-activity (kde-current-activity)))
Main major modes
mu4e
These are some standard config items
(require 'smtpmail)
(require 'mu4e)
(require 'helm-mu)
(global-set-key (kbd "C-x μ") 'mu4e)
(global-set-key (kbd "C-x 5 μ") (eval-with-new-frame (mu4e)))
(defalias 'μ 'mu4e)
(setq mu4e-maildir "~/.mail"
mu4e-attachment-dir "~/attachments"
mu4e-html2text-command "w3m -dump -T text/html -cols 72 -o display_link_number=true -o auto_image=false -o display_image=false -o ignore_null_img_alt=true"
mu4e-headers-show-threads nil
mu4e-headers-sort-direction "descending"
mail-user-agent 'mu4e-user-agent
read-mail-command 'mu4e
mu4e-update-interval 120
mu4e-view-use-gnus t
message-kill-buffer-on-exit t
mu4e-compose-format-flowed t
fill-flowed-encode-column 998
mm-text-html-renderer 'gnus-w3m
mu4e-read-option-use-builtin nil
mu4e-completing-read-function 'completing-read
mu4e-modeline-support nil
)
(let (p1 p2 myLine)
(setq p1 (line-beginning-position) )
(setq p2 (line-end-position) )
(setq myLine (buffer-substring-no-properties p1 p2)))
(defun get-buffer-current-line ()
(let ((p (line-beginning-position))
(q (line-end-position)))
(buffer-substring-no-properties p q)))
(defun get-buffer-to-eol ()
(let ((p (point))
(q (line-end-position)))
(buffer-substring-no-properties p q)))
(defun format-zoom-meeting ()
(if (boundp 'zoom-link)
(if (boundp 'zoom-passcode)
(concat zoom-summary
" [[" zoom-link "][link]] (" zoom-passcode ")\n SCHEDULED: " zoom-scheduled-time)
(concat zoom-summary
" [[" zoom-link "][link]]\n SCHEDULED: " zoom-scheduled-time))
(concat zoom-summary "\n SCHEDULED: " zoom-scheduled-time)))
(defun create-item-for-meeting (msg)
"Search for messages sent by the sender of the message at point."
(makunbound 'zoom-link)
(makunbound 'zoom-passcode)
(save-excursion
(beginning-of-buffer)
(cond ((search-forward "Join Zoom Meeting" nil t)
(message "found zoom meeting")
(right-char)
(setq zoom-link (get-buffer-current-line))
(when (search-forward "Passcode:" nil t)
(setq zoom-passcode (get-buffer-current-line)))
(beginning-of-buffer)
(search-forward "Time:")
(search-forward "<")
(left-char)
(setq zoom-scheduled-time (get-buffer-to-eol))
(search-backward "Summary:")
(right-word)
(right-word)
(left-word)
(setq zoom-summary (get-buffer-to-eol))
(org-capture nil "z"))
((search-forward "Accept ]")
(message "found generic meeting")
(search-forward "Summary:")
(right-word)
(right-word)
(left-word)
(setq zoom-summary (get-buffer-to-eol))
(search-forward "Location:")
(right-word)
(left-word)
(setq zoom-summary (concat zoom-summary " @" (get-buffer-to-eol)))
(search-forward "Time:")
(search-forward "<")
(left-char)
(setq zoom-scheduled-time (get-buffer-to-eol))
(org-capture nil "z")))))
;; define 'z' as the shortcut
(add-to-list 'mu4e-view-actions
'("zCreate agenda item for meeting" . create-item-for-meeting) t)
(advice-add 'mu4e-update-mail-and-index :around
(lambda (orig-fun &rest args)
"Only trigger an update if the server is the one that should
currently be active; otherwise each mu4e instance will trigger an
update"
(when (string-equal server-name (kde-current-activity-name))
(apply orig-fun args))))
(advice-add 'mu4e~header-line-format :around
(lambda (orig-fun &rest args)
(let ((mu4e-use-fancy-chars t)
(mu4e--mark-fringe-len 5))
(apply orig-fun args))))
Recipient handling
Add aliases (i.e. mailing groups) to the .mailrc file
This function inserts recipients name at point; might be useful with abbrev mode shortcuts
(load-file "~/.emacs.d/confidential.el")
(defun wilder/mu4e-process-recipient-name (name)
(interactive)
(let* ((clean-name (replace-regexp-in-string "\"" "" name))
(tentative-nickname (assoc clean-name nicknames-alist #'string-match-p)))
(cond
(tentative-nickname (cdr tentative-nickname))
((string-match-p (regexp-quote ",") clean-name) (string-trim (replace-regexp-in-string ".*," "" clean-name)))
(t (replace-regexp-in-string " .*" "" clean-name)))))
(defun recipients-ng ()
(interactive)
(save-excursion
(goto-char (point-min))
(search-forward-regexp "To: \\(\\(.\\|\n\\)*\\)\\(Subject\\|C(C\\|BCC\\)")
(mapconcat #'wilder/mu4e-process-recipient-name
(mapcar (lambda (s) (substring-no-properties
(string-trim (car (split-string s "<")))))
(mapcar #'string-trim (split-string (match-string 1) ">,")))
", ")))
This seems to be a very crude attempt, that is indeed what I am using right now.
(defun mu4e-insert-recipients-name ()
"Inserts the name of the recipients at point"
;; needs several improvements: at the moment it only handles very
;; basic situations (one recipient, single-word first name)
(insert (concat (recipients-ng) ", ")))
Customize mu4e bookmarks
(add-to-list 'mu4e-bookmarks
'( :name "Starred"
:query "flag:flagged"
:key ?s))
(add-to-list 'mu4e-bookmarks
'( :name "Archived, last two weeks"
:query "date:14d..now maildir:/math/Archive"
:key ?a))
(add-to-list 'mu4e-bookmarks
'( :name "to capture"
:query "maildir:/math/to-capture or maildir:/proton/Labels.capture"
:key ?c))
(add-to-list 'mu4e-bookmarks
'( :name "action-needed"
:query "maildir:/math/action-needed"
:key ?n))
(add-to-list 'mu4e-bookmarks
'( :name "inbox"
:query "maildir:/math/INBOX"
:key ?i))
Customize mu4e contexts
(setq mu4e-contexts
`( ,(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
:match-func (lambda (msg)
(when msg
(string-match-p "^/math/INBOX" (mu4e-message-field
msg :maildir))))
:vars '( ( user-mail-address . "jacopods@math.utoronto.ca" )
( user-full-name . "Jacopo De Simoi" )
( mu4e-sent-folder . "/math/Sent")
( mu4e-drafts-folder . "/math/Drafts")
( mu4e-trash-folder . "/math/Trash")
( mu4e-refile-folder . "/math/Archive")
( smtpmail-stream-type . starttls )
( smtpmail-smtp-service . 587 )
( mu4e-compose-signature . (concat
"Jacopo De Simoi (he · him)\n"
"Professor · U Toronto\n"))))
,(make-mu4e-context
:name "CMP Editor"
:enter-func
(lambda () (mu4e-message "Switch to the CMP Editor context"))
:leave-func
(lambda () (mu4e-message "Leave the CMP Editor context"))
;; we match based on the maildir of the message
:match-func (lambda (msg)
(when msg
(string-match-p "^/math/editorial.CMP" (mu4e-message-field
msg :maildir))))
:vars '( ( user-mail-address . "jacopods@math.utoronto.ca" )
( user-full-name . "Jacopo De Simoi" )
( mu4e-sent-folder . "/math/Sent")
( mu4e-drafts-folder . "/math/Drafts")
( mu4e-trash-folder . "/math/Trash")
( mu4e-refile-folder . "/math/editorial.CMP.Archive")
( smtpmail-stream-type . starttls )
( smtpmail-smtp-service . 587 )
( mu4e-compose-signature . (concat
"Jacopo De Simoi (he · him)\n"
"Associate Editor for CMP\n"
"Professor · U Toronto\n"))))))
;; 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 'ask-if-none)
;; compose with the current context is no context matches;
;; default is to ask
(setq mu4e-compose-context-policy 'ask-if-none)
(add-hook 'mu4e-headers-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
(add-hook 'mu4e-view-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
(add-hook 'mu4e-compose-mode-hook
#'(lambda ()
(auto-fill-mode -1)
(visual-line-mode)
;; flyspell does not want to work with buffers that
;; start with a "*", but this includes compose buffers
;; (that are called *draft*)
(let ((flyspell-lazy-disallow-buffers nil))
(flyspell-mode))))
(define-key-alist message-mode-map
'(("M-\"" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "“" "”" r-begin r-end)))))
Use mbsync te fetch emails from server
;; Get mail
(setq mu4e-get-mail-command "mbsync math-fast; mbsync proton-capture"
mu4e-change-filenames-when-moving t ; needed for mbsync
mu4e-headers-include-related nil ;do not include threaded
;sent messages in inbox queries
)
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile)
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile)
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile)
(define-key mu4e-view-mode-map (kbd "h") 'mu4e-view-mark-for-refile)
(define-key mu4e-headers-mode-map (kbd "r") 'mu4e-compose-reply)
(define-key mu4e-view-mode-map (kbd "r") 'mu4e-compose-reply)
(define-key mu4e-headers-mode-map (kbd "f") 'mu4e-compose-forward)
(define-key mu4e-headers-mode-map (kbd "C-k") 'mu4e-headers-mark-for-trash)
;; Send mail
(setq message-send-mail-function 'smtpmail-send-it
smtpmail-servers-requiring-authorization ".*"
smtpmail-smtp-server "smtp.math.toronto.edu")
;(require 'org-mu4e)
;;store link to message if in header view, not to header query
(setq mu4e-org-link-query-in-headers-mode nil)
This is supposed to make the calendar invitation buttons work.
(require 'mu4e-icalendar)
(mu4e-icalendar-setup)
This snippet filters stale addresses from address completion; I
found this method here. The code loads the definition of
filter-addresses from an external file and checks if addr is
one of the addresses in the list.
(defun wilder/save-filter-addresses ()
"Saves my projects in my home folder."
(interactive)
(with-temp-buffer
(insert (prin1-to-string filter-addresses))
(write-region (point-min)
(point-max)
"~/.emacs.d/filter-addresses.eld")))
(setq filter-addresses (car (read-from-string (with-temp-buffer
(insert-file-contents "~/.emacs.d/filter-addresses.eld")
(buffer-string)))))
(defun wilder/update-fiter-addresses-regex ()
(setq filter-addresses-regex
(mapconcat #'regexp-quote filter-addresses "\\\|")
helm-mu-contacts-ignore-candidates-regexp filter-addresses-regex))
(wilder/update-fiter-addresses-regex)
(defun my-mu4e-contact-filter-function (addr)
(unless (string-match-p filter-addresses-regex addr)
addr))
(setq mu4e-contact-process-function 'my-mu4e-contact-filter-function)
(defun wilder/add-address-to-filter (address)
(add-to-list 'filter-addresses address)
(wilder/save-filter-addresses)
(wilder/update-fiter-addresses-regex))
(defun wilder/add-candidate-to-filter (candidate)
(wilder/add-address-to-filter (car (split-string candidate "\t"))))
(helm-add-action-to-source "Filter away" #'wilder/add-candidate-to-filter helm-source-mu-contacts)
This snippet improves handling of links that span several lines.
It piggy-backs on gnus doing all the heavy lifting
(defun gnus-next-url ()
(gnus-text-property-search 'action 'browse-url t t))
(defun gnus-bounds-of-url-at-point ()
(let ((start (point))
(current-url (get-text-property (point) 'button-data)))
(gnus-text-property-search 'action 'browse-url t t t)
(while (and (= 1 (- (or (gnus-text-property-search 'action 'browse-url t) 0) (point)))
(string-equal current-url (get-text-property (1+ (point)) 'button-data)) )
(forward-char)
(gnus-text-property-search 'action 'browse-url t t t))
(cons start (point))))
(defun mu4e--view-activate-urls ()
"Turn things that look like URLs into clickable things.
Also number them so they can be opened using `mu4e-view-go-to-url'."
(let ((num 0)
(use-gnus-properties mu4e-view-use-gnus))
(save-excursion
(setq mu4e--view-link-map ;; buffer local
(make-hash-table :size 32 :weakness nil))
(goto-char (point-min))
(while (if use-gnus-properties (gnus-next-url)
(re-search-forward mu4e--view-beginning-of-url-regexp nil t))
(let ((bounds (if use-gnus-properties (gnus-bounds-of-url-at-point)
(thing-at-point-bounds-of-url-at-point))))
(when bounds
(let* ((url (or (get-text-property (car bounds) 'button-data)
(thing-at-point-url-at-point)))
(ov (make-overlay (car bounds) (cdr bounds))))
(when (string-match-p "https?" url)
(puthash (cl-incf num) url mu4e--view-link-map)
(add-text-properties
(car bounds)
(cdr bounds)
`(face mu4e-link-face
mouse-face highlight
mu4e-url ,url
keymap ,mu4e-view-active-urls-keymap
help-echo
"[mouse-1] or [M-RET] to open the link"))
(overlay-put ov 'mu4e-overlay t)
(overlay-put ov 'after-string
(propertize (format "\u200B[%d]" num)
'face 'mu4e-url-number-face))))))))))
This one kills servers attached to mu4e instances in other activities
(defun mu4e--server-sentinel (proc _msg)
"Function called when the server process PROC terminates with MSG."
(let ((status (process-status proc)) (code (process-exit-status proc)))
(setq mu4e--server-process nil)
(setq mu4e--server-buf "") ;; clear any half-received sexps
(cond
((eq status 'signal)
(cond
((or(eq code 9) (eq code 2)) (message nil))
;;(message "the mu server process has been stopped"))
(t (error (format "mu server process received signal %d" code)))))
((eq status 'exit)
(cond
((eq code 0)
(message nil)) ;; don't do anything
((eq code 19)
(when (string-equal (kde-current-activity-name) server-name)
(start-process "killer" nil "killall" "mu"))
(error "Database is locked by another process; try again"))
(t (error "Mu server process ended with exit code %d" code))))
(t
(error "Something bad happened to the mu server process")))))
org-mode
Require
Require the org package;
(with-eval-after-load "org"
(define-key org-src-mode-map (kbd "C-c C-c") 'org-edit-src-exit)
(define-key org-src-mode-map (kbd "C-c C-k") nil) ;this conflicts with my LaTeX bindings
(define-key org-src-mode-map (kbd "C-c C-`") 'org-edit-src-abort))
(require 'org)
(require 'org-habit)
Hooks
Enable org-inline-pdf
(add-hook 'org-mode-hook #'org-inline-pdf-mode)
Enable auto-fill-mode (see [the manual]: honestly I do not see
where I would not want to use this feature).
(add-hook 'org-mode-hook 'turn-on-auto-fill)
It sometimes happen (e.g. when opening files from magit) that
the point is in an invisible region; then the usual
smart-line-beginning would get upset. This variation takes care
of the problem.
(defun org-smart-line-beginning ()
(interactive)
(when (or (invisible-p (point))
(invisible-p (- (point) 1)))
;; assume that we are in a folded headline; move back to heading
;; otherwise we will be trapped in the invisible region
(org-back-to-heading))
(smart-line-beginning))
(define-key org-mode-map (kbd "C-a") 'org-smart-line-beginning)
Disable truncate-lines
(setq org-starting-truncated nil
org-startup-folded t)
Helper for quotes
(define-key-alist org-mode-map
'(("M-\"" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "“" "”" r-begin r-end)))))
Cosmetics
Change the default ellipsis ... to the unicode ellipsis … and
set my preferred indentation
(setq org-ellipsis "…"
org-adapt-indentation t
org-tags-column -75)
Set the image width to fill the window (this may not be OK for everything, but it works for zettels)
(setq org-image-actual-width '(1.0))
Ligatures [not in working order]
These require a patched hackminus font. Too bad it does not work
for some reason
;; (defun delayed-org-prettify ()
;; (prettify-symbols-mode 0)
;; (run-with-idle-timer 0 nil
;; (lambda ()
;; (prettify-symbols-mode 1)
;; (add-to-list 'prettify-symbols-alist '("**" . (? (Br . Bl) ?)))
;; (add-to-list 'prettify-symbols-alist '("***" . (? (Br . Bl) ? (Br . Bl) ?)))
;; (add-to-list 'prettify-symbols-alist '("****" . (? (Br . Bl) ? (Br . Bl) ? (Br . Bl) ?))))))
;;(add-hook 'org-mode-hook 'delayed-org-prettify)
Links
Open links with external stuff
(setq org-file-apps
'((auto-mode . emacs)
("\\.x?html?\\'" . "xdg-open %s")
("\\.djvu\\'" . "xdg-open \"%s\"")
("\\.pdf\\'" . "xdg-open \"%s\"")))
Agenda
Set the canonical binding for the agenda
(global-set-key (kbd "C-c a") 'org-agenda)
Define agenda files: the main one is master.org, but then I have
a bunch of them on orgzly
(setq org-agenda-files
'("~/org/master.org"
"~/org/orgzly/work.org"
"~/org/orgzly/hack.org"
"~/org/orgzly/library.org"
"~/org/kaizen.org"
"~/org/editorial.org"
"~/clones/orgzly/open-loops.org"
"~/clones/orgzly/refile-git.org"
"~/clones/orgzly/calendar.org"
"~/clones/orgzly/not-now.org"
"~/clones/orgzly/reference.org"))
Use less verbose reminders:
(setq org-agenda-deadline-leaders
'("⇒" "@ %3d d. " "! %2d d. ago ")
org-agenda-scheduled-leaders
'("→" "↻ %2d×")
org-agenda-habit-leader "·")
Default to daily agenda
(setq org-agenda-span 1)
Prevent headlines to be marked as DONE if some sub-headings are still TODO.
(setq org-enforce-todo-dependencies t)
Do not show scheduled or deadline'd stuff in the Global TODO list
(setq org-agenda-todo-ignore-deadlines 'all
org-agenda-todo-ignore-scheduled 'all)
Do not show stuff marked with DONE even if they have a deadline
(setq org-agenda-skip-deadline-if-done t)
Custom separator
(setq org-agenda-block-separator ?─)
Turn on speed keys
(setq org-use-speed-commands t)
(setq org-agenda-sorting-strategy
'((agenda habit-down deadline-up time-up scheduled-up priority-down category-keep)
(todo priority-down category-keep)
(tags priority-down category-keep)
(search category-keep)))
Customize the agenda interface a bit
-
add a newline before habits
(defun wilder/insert-before-first (pred el list &optional base) "Returns a list in which EL is inserted before the first occurrence in LIST satisfying PRED" (if (not list) base (if (funcall pred (car list)) (append base (list el) list) (wilder/insert-before-first pred el (cdr list) (nconc base (list (car list))))))) (defun org-agenda-finalize-entries (list &optional type) "Sort, limit and concatenate the LIST of agenda items. The optional argument TYPE tells the agenda type." (setq org-planner list) (let ((max-effort (cond ((listp org-agenda-max-effort) (cdr (assoc type org-agenda-max-effort))) (t org-agenda-max-effort))) (max-todo (cond ((listp org-agenda-max-todos) (cdr (assoc type org-agenda-max-todos))) (t org-agenda-max-todos))) (max-tags (cond ((listp org-agenda-max-tags) (cdr (assoc type org-agenda-max-tags))) (t org-agenda-max-tags))) (max-entries (cond ((listp org-agenda-max-entries) (cdr (assoc type org-agenda-max-entries))) (t org-agenda-max-entries)))) (when org-agenda-before-sorting-filter-function (setq list (delq nil (mapcar org-agenda-before-sorting-filter-function list)))) (setq list (mapcar #'org-agenda-highlight-todo list) list (mapcar #'identity (sort list #'org-entries-lessp))) (when max-effort (setq list (org-agenda-limit-entries list 'effort-minutes max-effort (lambda (e) (or e (if org-agenda-sort-noeffort-is-high 32767 -1)))))) (when max-todo (setq list (org-agenda-limit-entries list 'todo-state max-todo))) (when max-tags (setq list (org-agenda-limit-entries list 'tags max-tags))) (when max-entries (setq list (org-agenda-limit-entries list 'org-hd-marker max-entries))) (when (and org-agenda-dim-blocked-tasks org-blocker-hook) (setq list (mapcar #'org-agenda--mark-blocked-entry list))) ; the bit below adds a blank line before the first habit (setq list (wilder/insert-before-first #'(lambda (string) (get-text-property 0 'org-habit-p string)) "" list)) ; the bit below adds a blank line before the first scheduled item (mapconcat #'identity list "\n"))) -
Sleeker time-grid
(setq org-agenda-sort-notime-is-late nil)(setq org-agenda-time-grid '((daily today remove-match) (800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 830 930 1030 1130 1230 1330 1430 1530 1630 1730 1830 1930) " ······· " "────────────────────────────────────") org-agenda-current-time-string "····· now ·····" org-agenda-time-leading-zero t) -
Use figlet-type fonts to display the date
(setq figlet [["███" "██ " "███" "███" "█ █" "███" "███" "███" "███" "███"] ["█ █" " █ " " █" " █" "█ █" "█ " "█ " " █" "█ █" "█ █"] ["█ █" " █ " "███" "███" "███" "███" "███" " █" "███" "███"] ["█ █" " █ " "█ " " █" " █" " █" "█ █" " █" "█ █" " █"] ["███" "███" "███" "███" " █" "███" "███" " █" "███" "███"]]) (setq figlet-lean [["┌──┐" "╶┐ " "╶──┐" "╶──┐" "╷ ╷" "┌──╴" "┌──╴" "╶──┐" "┌──┐" "┌──┐"] ["│ │" " │ " " │" " │" "│ │" "│ " "│ " " │" "│ │" "│ │"] ["│ │" " │ " "┌──┘" "╶──┤" "└──┤" "└──┐" "├──┐" " │" "├──┤" "└──┤"] ["│ │" " │ " "│ " " │" " │" " │" "│ │" " │" "│ │" " │"] ["└──┘" "╶┴╴" "└──╴" "╶──┘" " ╵" "╶──┘" "└──┘" " ╵" "└──┘" "╶──┘"]]) (defun figlet-digit (digit row) (aref (aref figlet-lean row) digit)) (defun figlet-num (number row &optional padding) (let ((n number) (padding (or padding 0)) (d (list))) (while (or (> padding 0) (> n 0)) (setq padding (- padding 1) d (append (list (mod n 10)) d) n (/ n 10))) (string-join (mapcar (lambda (digit) (concat (figlet-digit digit row))) d) " "))) (defun org-agenda-format-date-aligned (date) "Format a DATE string for display in the daily/weekly agenda. This function makes sure that dates are aligned for easy reading." (require 'cal-iso) (let* ((dayname (calendar-day-name date)) (day (cadr date)) (day-of-week (calendar-day-of-week date)) (month (car date)) (monthname (calendar-month-name month)) (year (nth 2 date)) (iso-week (org-days-to-iso-week (calendar-absolute-from-gregorian date))) ;; (weekyear (cond ((and (= month 1) (>= iso-week 52)) ;; (1- year)) ;; ((and (= month 12) (<= iso-week 1)) ;; (1+ year)) ;; (t year))) (weekstring (format " W%02d" iso-week) )) (format "%s%s - %4d·%02d·%02d" dayname weekstring year month day))) (defun org-agenda-format-date-figlet (date) "Format a DATE string for display in the daily/weekly agenda. This function makes sure that dates are aligned for easy reading." (if (not (eq 'day org-agenda-current-span)) (let ((date-string (org-agenda-format-date-aligned date))) (concat "\n" date-string " " (make-string (- (window-body-width) 1 (length date-string)) ?─ "\n"))) (let* ((dayname (calendar-day-name date)) (day (cadr date)) (day-of-week (calendar-day-of-week date)) (month (car date)) (monthname (calendar-month-name month)) (year (nth 2 date)) (iso-week (org-days-to-iso-week (calendar-absolute-from-gregorian date))) (weekyear (cond ((and (= month 1) (>= iso-week 52)) (1- year)) ((and (= month 12) (<= iso-week 1)) (1+ year)) (t year))) (weekstring (format " W%02d" iso-week))) ;; (format (concat "\n" ;; "%9s %s %4d%s\n" ;; "%9s \n" ;; "%9s %-10s\n" ;; "%9s \n" ;; "%9s %s\n") ;; (figlet-num day 0) monthname year weekstring ;; (figlet-num day 1) ;; (figlet-num day 2) dayname ;; (figlet-num day 3) ;; (figlet-num day 4) (sunrise-sunset)) (let ((solar-info (solar-sunrise-sunset-string-list (calendar-current-date))) (spacer " ") ) (concat "\n" (format " %4d·%02d " year month) "\n" (format "%9s" (figlet-num day 0 2)) spacer (format "%s%s" dayname weekstring) "\n" (format "%9s" (figlet-num day 1 2)) spacer (car solar-info) "\n" (format "%9s" (figlet-num day 2 2)) spacer (cadr solar-info) "\n" (format "%9s" (figlet-num day 3 2)) spacer (caddr solar-info) "\n" (format "%9s" (figlet-num day 4 2)) "\n" ))))) (require 'solar) (defun solar-sunrise-sunset-string-list (date &optional nolocation) "String of *local* times of sunrise, sunset, and daylight on Gregorian DATE. Optional NOLOCATION non-nil means do not print the location." (let ((l (solar-sunrise-sunset date))) (list (if (car l) (concat "Sunrise " (apply #'solar-time-string (car l))) "No sunrise") (if (cadr l) (concat "Sunset " (apply #'solar-time-string (cadr l))) "no sunset") (format "%s hours daylight" (nth 2 l))))) (solar-sunrise-sunset-string-list (calendar-current-date)) (defun left-right-align (s1 s2) (concat s1 (make-string (- (window-body-width) (length s1) (length s2)) ?\ ) s2 "\n")) (setq calendar-time-display-form '(24-hours ":" minutes (if time-zone " (") time-zone (if time-zone ")"))) (setq org-agenda-format-date #'org-agenda-format-date-figlet) (setq org-agenda-prefix-format '((agenda . " %i %6c %s %?-12t") (todo . " %i %6c · ") (tags . " %i %6c · ") (search . " %i %6c · "))) (advice-add 'org-agenda-get-scheduled :around (lambda (orig-fun &rest args) "Only show habits on day wiew, not on week view" (let ((org-habit-show-habits (with-current-buffer org-agenda-buffer (eq org-agenda-current-span 'day)))) (apply orig-fun args)))) -
Add a whiteline after the header Now, this is a horrible hack: the string that forms the header is obtained by constructing some pretty-text in a temporary buffer and then retrieving the string with #'buffer-string Hence we add (and remove) an advice to the function buffer-string that concat's a newline around each function that produces the headers
(defun concat-newline (s) (concat s "\n")) (dolist (fun '(org-tags-view org-todo-list)) (advice-add fun :around (lambda (orig-fun &rest args) (advice-add #'buffer-string :filter-return #'concat-newline) (apply orig-fun args) (advice-remove #'buffer-string #'concat-newline))))(advice-add #'org-agenda--insert-overriding-header :filter-args (lambda (params) (list `(concat ,(car params) "\n"))))(defun wilder/delta-list (l op) (unless (< (length l) 2) (cons (funcall op (car l) (cadr l)) (wilder/delta-list (cdr l) op)))) (defun wilder/truncate-list-when (l predicate) (unless (funcall predicate (car l)) (cons (car l) (wilder/truncate-list-when (cdr l) predicate)))) (defun wilder/habit-streak (habit) (let* ((done-dates (sort (org-habit-done-dates habit) #'>)) (dead-rep (org-habit-deadline-repeat habit)) (sched-rep (org-habit-scheduled-repeat habit)) (today-date (time-to-days nil)) (done-list (append (list today-date) done-dates '(0))) (delta-list (wilder/delta-list done-list #'-)) (truncated-list (wilder/truncate-list-when delta-list (lambda (x) (> x dead-rep))))) (list (length truncated-list) (list (car delta-list) sched-rep dead-rep))))
Improve the org-habit graph display
(setq org-habit-regular-glyph ?□
org-habit-today-glyph ?
org-habit-completed-glyph ?▣
org-habit-completed-today-glyph ?
org-habit-preceding-days 6
org-habit-graph-column 62)
(defun wilder/org-habit-build-graph (habit starting current ending)
"Build a graph for the given HABIT, from STARTING to ENDING.
CURRENT gives the current time between STARTING and ENDING, for
the purpose of drawing the graph. It need not be the actual
current time."
(let* ((all-done-dates (sort (org-habit-done-dates habit) #'<))
(done-dates all-done-dates)
(scheduled (org-habit-scheduled habit))
(s-repeat (org-habit-scheduled-repeat habit))
(start (time-to-days starting))
(now (time-to-days current))
(end (time-to-days ending))
(graph (make-string (- end start) org-habit-regular-glyph))
(index 0)
last-done-date)
(while (and done-dates (< (car done-dates) start))
(setq last-done-date (car done-dates)
done-dates (cdr done-dates)))
(while (< start end)
(let* ((in-the-past-p (< start now))
(todayp (= start now))
(donep (and done-dates (= start (car done-dates))))
(faces
(if (and in-the-past-p
(not last-done-date)
(not (< scheduled now)))
(if (and all-done-dates (= (car all-done-dates) start))
;; This is the very first done of this habit.
'(org-habit-ready-face . org-habit-ready-future-face)
'(org-habit-clear-face . org-habit-clear-future-face))
(org-habit-get-faces
habit start
(and in-the-past-p
last-done-date
;; Compute scheduled time for habit at the time
;; START was current.
(let ((type (org-habit-repeat-type habit)))
(cond
;; At the last done date, use current
;; scheduling in all cases.
((null done-dates) scheduled)
((equal type ".+") (+ last-done-date s-repeat))
((equal type "+")
;; Since LAST-DONE-DATE, each done mark
;; shifted scheduled date by S-REPEAT.
(- scheduled (* (length done-dates) s-repeat)))
(t
;; Compute the scheduled time after the
;; first repeat. This is the closest time
;; past FIRST-DONE which can reach SCHEDULED
;; by a number of S-REPEAT hops.
;;
;; Then, play TODO state change history from
;; the beginning in order to find current
;; scheduled time.
(let* ((first-done (car all-done-dates))
(s (let ((shift (mod (- scheduled first-done)
s-repeat)))
(+ (if (= shift 0) s-repeat shift)
first-done))))
(if (= first-done last-done-date) s
(catch :exit
(dolist (done (cdr all-done-dates) s)
;; Each repeat shifts S by any
;; number of S-REPEAT hops it takes
;; to get past DONE, with a minimum
;; of one hop.
(cl-incf s (* (1+ (/ (max (- done s) 0)
s-repeat))
s-repeat))
(when (= done last-done-date)
(throw :exit s))))))))))
donep)))
markedp face)
(cond
(donep
(aset graph index (if todayp org-habit-completed-today-glyph org-habit-completed-glyph))
(setq markedp t)
(while (and done-dates (= start (car done-dates)))
(setq last-done-date (car done-dates))
(setq done-dates (cdr done-dates))))
(todayp
(aset graph index org-habit-today-glyph)))
(setq face (if (or in-the-past-p todayp)
(car faces)
(cdr faces)))
(when (and in-the-past-p
(not (eq face 'org-habit-overdue-face))
(not markedp))
(setq face (cdr faces)))
(put-text-property index (1+ index) 'face face graph)
)
(setq start (1+ start)
index (1+ index)))
(let* ((streak (wilder/habit-streak habit))
(streak-current (car streak))
(streak-current-stats (nth 1 streak))
(current-ok (< (car streak-current-stats) (nth 1 streak-current-stats)))
(current-on-deadline (= (car streak-current-stats) (nth 2 streak-current-stats)))
(streak-face (if (eq 0 streak-current) 'font-lock-comment-face
(if current-on-deadline 'org-habit-overdue-face
(if current-ok 'org-habit-ready-face
'org-habit-alert-face))))
(streak-string (format "%4d" streak-current)))
(put-text-property 0 4 'face streak-face streak-string)
(concat streak-string "·" graph))))
(advice-add 'org-habit-build-graph :override #'wilder/org-habit-build-graph)
Ideally I should tag some tasks as “break” tasks, which are
suitable to be taken care of during a pomodoro break. Such tasks
should be marked with tags :5m: and :20m: according to the
estimate on the time it would take to take care of them
Is it possible to edit the default agenda views?
(setq org-agenda-custom-commands
'(
("a" "Main agenda"
((agenda "")
(todo "NEXT|ONGOING")
(tags-todo "hack")
(tags-todo "5m")
(todo "TODO")))
("h" "Just the agenda"
((agenda "")))
("5" "Agenda and Break tasks"
((agenda "")
(tags-todo "5m")
(tags-todo "20m")))))
Custom agenda view
- cleanup stuff (TODO, what is this doing, exactly? )
(defun org-agenda-prepare (&optional name)
(let ((filter-alist (if org-agenda-persistent-filter
(with-current-buffer
(get-buffer-create org-agenda-buffer-name)
(list `(tag . ,org-agenda-tag-filter)
`(re . ,org-agenda-regexp-filter)
`(effort . ,org-agenda-effort-filter)
`(cat . ,org-agenda-category-filter))))))
(if (org-agenda-use-sticky-p)
(progn
(put 'org-agenda-tag-filter :preset-filter nil)
(put 'org-agenda-category-filter :preset-filter nil)
(put 'org-agenda-regexp-filter :preset-filter nil)
;; Popup existing buffer
(org-agenda-prepare-window (get-buffer org-agenda-buffer-name)
filter-alist)
(message "Sticky Agenda buffer, use `r' to refresh")
(or org-agenda-multi (org-agenda-fit-window-to-buffer))
(throw 'exit "Sticky Agenda buffer, use `r' to refresh"))
(setq org-todo-keywords-for-agenda nil)
(put 'org-agenda-tag-filter :preset-filter
org-agenda-tag-filter-preset)
(put 'org-agenda-category-filter :preset-filter
org-agenda-category-filter-preset)
(put 'org-agenda-regexp-filter :preset-filter
org-agenda-regexp-filter-preset)
(put 'org-agenda-effort-filter :preset-filter
org-agenda-effort-filter-preset)
(if org-agenda-multi
(progn
(setq buffer-read-only nil)
(goto-char (point-max))
(unless (or (bobp) org-agenda-compact-blocks
(not org-agenda-block-separator))
(insert "\n"
(if (stringp org-agenda-block-separator)
org-agenda-block-separator
(make-string (window-width) org-agenda-block-separator))
"\n\n"))
(narrow-to-region (point) (point-max)))
(setq org-done-keywords-for-agenda nil)
;; Setting any variables that are in org-agenda-local-vars
;; list need to be done after the prepare call
(org-agenda-prepare-window
(get-buffer-create org-agenda-buffer-name) filter-alist)
(setq buffer-read-only nil)
(org-agenda-reset-markers)
(let ((inhibit-read-only t)) (erase-buffer))
(org-agenda-mode)
(setq org-refile-targets '((nil . (:maxlevel . 5))
(org-agenda-files . (:maxlevel . 1))))
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps nil)
(setq org-agenda-buffer (current-buffer))
(setq org-agenda-contributing-files nil)
(setq org-agenda-columns-active nil)
(org-agenda-prepare-buffers (org-agenda-files nil 'ifmode))
(setq org-todo-keywords-for-agenda
(org-uniquify org-todo-keywords-for-agenda))
(setq org-done-keywords-for-agenda
(org-uniquify org-done-keywords-for-agenda))
(setq org-agenda-last-prefix-arg current-prefix-arg)
(setq org-agenda-this-buffer-name org-agenda-buffer-name)
(and name (not org-agenda-name)
(setq-local org-agenda-name name)))
(setq buffer-read-only nil))))
(add-hook 'org-agenda-mode-hook
(lambda () (setq truncate-lines t
show-trailing-whitespace nil)))
Automate saving
Save all org buffers with the auto-save-hook. Avoid saving the buffer that is currently being edited; doing so removes trailing whitespace, which is undesirable while writing.
(defun org-save-all-org-buffers-except-current ()
"Save all Org buffers without user confirmation."
(interactive)
(message "Saving all Org buffers...")
(let ((buffer (current-buffer)))
(save-some-buffers t (lambda () (and (not (eq (current-buffer) buffer)) (derived-mode-p 'org-mode)))))
(when (featurep 'org-id) (org-id-locations-save))
(message "Saving all Org buffers... done"))
(add-hook 'auto-save-hook 'org-save-all-org-buffers-except-current)
Also, we need to trigger saving once the state changes, so that we are always able to sync the right state; saving runs with a timer so that it is not triggered e.g. when cycling through states. The problem with the current implementation is that we do not know which buffer has been modified if the state has been changed using the agenda, which is kind of the point…
(add-hook 'org-after-todo-state-change-hook
(lambda ()
(when buffer-file-name (save-buffer-with-timer 5))))
cycling-headlines
This bit is useful to cycle headings in files; the interesting variable should be buffer-local
(setq org-heading-cycle-list nil)
(defun next-heading-in-cycle-list (heading cycle-list)
(when cycle-list
(or (next-heading-in-cycle heading (car cycle-list))
(next-heading-in-cycle-list heading (cdr cycle-list)))))
(defun next-heading-in-cycle (heading cycle)
"Returns the next heading in the cycle or nil if none"
(let ((pos (cl-position heading cycle :test 'equal))
(len (length cycle)))
(when pos
(let ((next (mod (+ 1 pos) len)))
(nth next cycle)))))
(defun wilder/org-cycle-headlines ()
(when (equal org-state "DONE")
(let* ((current-heading (org-entry-get nil "ITEM"))
(next-heading (next-heading-in-cycle-list current-heading org-heading-cycle-list)))
(when next-heading
(org-edit-headline next-heading)))))
(add-hook 'org-after-todo-state-change-hook
#'wilder/org-cycle-headlines)
Reverting stuff from orgzly
(defun org-revert-all-orgzly-buffers (&optional ALL)
"Revert all Org buffers that are sitting in orgzly
Prompt for confirmation when there are unsaved changes. Be sure
you know what you are doing before letting this function
overwrite your changes."
(interactive)
(message "Reverting Orgzly buffers…")
; pull from remote
(let ((default-directory "~/clones/orgzly"))
(or (eq 0 (shell-command "git pull origin master"))
(call-interactively #'magit-status)))
(dolist (b (buffer-list))
(when (and (with-current-buffer b (derived-mode-p 'org-mode))
(with-current-buffer b buffer-file-name)
(with-current-buffer b (string-match-p "orgzly" default-directory)))
(with-current-buffer b
(revert-buffer t 'no-confirm))))
(org-agenda-redo-all)
(org-agenda-redo-all))
;; I do not understand why, but I need to run it twice
(define-key org-agenda-mode-map (kbd "Z") 'org-revert-all-orgzly-buffers)
Use gac
(require 'git-auto-commit-mode)
(setq gac-automatically-push-p t)
Hydras for timestamps
(defun hydra-timestamp/hl-paren-force-fix ()
;;; This is needed b/c hl-parent caches the point position and
;;; refuses to update if the point did not move
(let ((hl-paren-last-point -1))
(hl-paren-highlight)))
(defun hydra-timestamp/pre ()
(unless (boundp 'hydra-timestamp/hpm)
(highlight-parentheses-mode 1))
(setq hydra-timestamp/hpm t))
(defun hydra-timestamp/post ()
(highlight-parentheses-mode -1)
(makunbound 'hydra-timestamp/hpm))
(global-set-key
(kbd "C-α")
(defhydra hydra-timestamp
(:pre hydra-timestamp/pre :post hydra-timestamp/post)
"paren slurp and barf"
("i" (progn
(org-timestamp-up-day)
(hydra-timestamp/hl-paren-force-fix)) "+1D")
("n" (progn
(org-timestamp-down-day)
(hydra-timestamp/hl-paren-force-fix)) "-1D")
("t" (progn
(org-timestamp-up-day 7)
(hydra-timestamp/hl-paren-force-fix)) "+1W")
("r" (progn
(org-timestamp-down-day 7)
(hydra-timestamp/hl-paren-force-fix)) "-1W")))
Hydras for refiling
This bit evolved from this post; see more at this gist.
(defun wilder/refile (file headline &optional arg)
(let ((pos (save-excursion
(find-file file)
(org-find-exact-headline-in-buffer headline))))
(org-refile arg nil (list headline file nil pos)))
(switch-to-buffer (current-buffer)))
(defhydra wilder/hydra-org-refile (:foreign-keys run)
"Refile"
("g" (wilder/refile "hack.org" "emacs") "Refile to Hack-emacs")
("q" nil "cancel"))
(define-key org-mode-map (kbd "C-x \\") 'wilder/hydra-org-refile/body)
Hydras for moving headlines around
(defhydra wilder/hydra-org-move ()
"Move subtree"
("p" outline-previous-visible-heading "move up" :column "Movement")
("n" outline-next-visible-heading "move down")
("r" outline-up-heading "level up")
("f" org-promote-subtree "promote" :column "Bubble")
("s" org-demote-subtree "demote")
("i" org-move-subtree-up "bubble up")
("e" org-move-subtree-down "bubble down")
("h" org-archive-subtree "archive" :column "Edit")
("d" kill-line "kill")
("q" nil "cancel"))
(define-key org-mode-map (kbd "C-x SPC") 'wilder/hydra-org-move/body)
tags-to-filename
(defun org-tags-to-filenames ()
(message "parsing")
(sleep-for 1)
(let ((tags (car (last (org-heading-components)))))
(when (stringp tags)
(remove nil (mapcar
(lambda (tag)
(cdr (assoc tag tag-to-filenames-alist)))
(org-split-string tags ":"))))))
Capture
Set default keybinding
(global-set-key (kbd "C-c c") 'org-capture)
This is my capture template: it needs to be revised as I really do not use The Idea, journal and break entry
(setq org-default-notes-file "~/org/master.org")
(setq wilder/org-calendar-file "~/clones/orgzly/calendar.org")
(setq org-capture-templates
`(("t" "TODO today" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* TODO %?\n SCHEDULED: %t\n %a" :clock-in t :clock-resume t)
("N" "DOING right now" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* ONGOING %?\n SCHEDULED: %T\n %a" :clock-in t :clock-keep t)
("n" "TODO next" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* NEXT %?\n " :clock-in t :clock-resume t)
("T" "TODO" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* TODO %?\n %a" :clock-in t :clock-resume t)
("M" "TENTATIVE" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* TENTATIVE %?\n %a" :clock-in t :clock-resume t)
("i" "Idea" entry (file+headline "~/org/master.org" "Ideas")
"* IDEA %?\n %u" :clock-in t :clock-resume t )
("j" "Journal" entry (file+datetree "~/org/master.org")
"* %?\n%U\n" :clock-in t :clock-resume t)
("b" "Break" entry (file+datetree "~/org/master.org")
"* break %?\n" :clock-in t :clock-resume t)
("e" "Mail To" entry (file+headline ,wilder/org-calendar-file "E-mails")
"* DONE mailto:%?")
("R" "Reply" entry (file+headline ,wilder/org-calendar-file "E-Mails")
"* TODO Reply to %:from \nSCHEDULED: %(org-insert-time-stamp
(org-read-date nil t \"+0d\"))\n%a\n%?\n")
("z" "Zoom meeting" entry (file+headline ,wilder/org-calendar-file "Tasks")
"* TODO Attend %(format-zoom-meeting)\n%a" :immediate-finish t)))
LaTeX export
; (add-to-list 'org-latex-classes
; '("amsart" "\\documentclass[11pt]{amsart}"
; ("\\section{%s}" . "\\section*{%s}")
; ("\\subsection{%s}" . "\\subsection*{%s}")
; ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
; ("\\paragraph{%s}" . "\\paragraph*{%s}")
; ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
Source blocks
Add template for a source block in a few selected languages; for now I have
- emacs-lisp
- shell
. This is useful for writing the emacs init file in literate form,
or dotfiles for other code. Since Org 9.2, the behavior that I
grew accustomed to (e.g. <el TAB) )is implemented by
org-tempo, so I need to require that as well.
(dolist (structure-template '(("el" . "src emacs-lisp")
("sh" . "src sh")
("ss" . "src scheme")
("py" . "src python")
("χ" . "src LaTeX")))
(add-to-list 'org-structure-template-alist structure-template))
(require 'org-tempo)
Fontify src blocks
(setq org-src-fontify-natively t)
This makes scheme src blocks work
(org-babel-do-load-languages
'org-babel-load-languages
'((python . t)
(scheme . t)
(emacs-lisp . t)))
Patch ob-scheme.el while waiting for upstream
(defun org-babel-scheme-execute-with-geiser (code output impl repl)
"Execute code in specified REPL.
If the REPL doesn't exist, create it using the given scheme
implementation.
Returns the output of executing the code if the OUTPUT parameter
is true; otherwise returns the last value."
(let ((result nil))
(with-temp-buffer
(insert (format ";; -*- geiser-scheme-implementation: %s -*-" impl))
(newline)
(insert code)
(geiser-mode)
(let ((geiser-repl-window-allow-split nil)
(geiser-repl-use-other-window nil))
(let ((repl-buffer (save-current-buffer
(org-babel-scheme-get-repl impl repl))))
(when (not (eq impl (org-babel-scheme-get-buffer-impl
(current-buffer))))
(message "Implementation mismatch: %s (%s) %s (%s)" impl (symbolp impl)
(org-babel-scheme-get-buffer-impl (current-buffer))
(symbolp (org-babel-scheme-get-buffer-impl
(current-buffer)))))
(setq geiser-repl--repl repl-buffer)
(setq geiser-impl--implementation nil)
(let ((geiser-debug-jump-to-debug-p nil)
(geiser-debug-show-debug-p nil))
(let ((ret (funcall
;; use `geiser-eval-region/wait' only when available
;; in newer versions of `geiser'
(if (fboundp 'geiser-eval-region/wait)
'geiser-eval-region/wait
'geiser-eval-region)
(point-min)
(point-max))))
(setq result (if output
(or (geiser-eval--retort-output ret)
"Geiser Interpreter produced no output")
(geiser-eval--retort-result-str ret "")))))
(when (not repl)
(save-current-buffer (set-buffer repl-buffer)
(geiser-repl-exit))
(set-process-query-on-exit-flag (get-buffer-process repl-buffer) nil)
(kill-buffer repl-buffer)))))
result))
Lowercase org blocks
There is a new trend to use lowercase in the org block definitions; this gist (taken from the Scripter blog) lowercases all instances in the current buffer
(defun modi/lower-case-org-keywords ()
"Lower case Org keywords and block identifiers.
Example: \"#+TITLE\" → \"#+title\"
\"#+BEGIN_EXAMPLE\" → \"#+begin_example\"
Inspiration:
https://code.orgmode.org/bzg/org-mode/commit/13424336a6f30c50952d291e7a82906c1210daf0."
(interactive)
(save-excursion
(goto-char (point-min))
(let ((case-fold-search nil)
(count 0))
;; Match examples: "#+foo bar", "#+foo:", "=#+foo=", "~#+foo~",
;; "‘#+foo’", "“#+foo”", ",#+foo bar",
;; "#+FOO_bar<eol>", "#+FOO<eol>".
(while (re-search-forward "\\(?1:#\\+[A-Z_]+\\(?:_[[:alpha:]]+\\)*\\)\\(?:[ :=~’”]\\|$\\)" nil :noerror)
(setq count (1+ count))
(replace-match (downcase (match-string-no-properties 1)) :fixedcase nil nil 1))
(message "Lower-cased %d matches" count))))
Clocking
Look into a possible enhancement of remove-zero-time-clocks
;; Separate drawers for clocking and logs
(setq org-drawers (quote ("PROPERTIES" "CLOCKBOOK")))
;; Save clock data and state changes and notes in the CLOCK drawer
(setq org-clock-into-drawer "CLOCKBOOK")
;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration
(setq org-clock-out-remove-zero-time-clocks t)
;; Clock out when moving task to a done state
(setq org-clock-out-when-done t)
(setq org-clock-persist 'history)
(setq org-duration-format 'h:mm)
(setq org-clock-clocktable-default-properties '(:maxlevel 2 :scope subtree))
(org-clock-persistence-insinuate)
COMMENT Tangle to different files
This has been broken by commit 3ebee033103ccd3c3e8c354bad01c15332b9d901 and a2cb9b853d30fc301f4553d1556dba4ee6bc1ead. Try some workarounds to pinpoint the issue:
(setq org-babel-tangle-use-relative-file-links nil)
—-
This is some some super-clever stuff. See [https://emacs.stackexchange.com/questions/39032/tangle-the-same-src-block-to-different-files]
(defun org-babel-tangle-collect-blocks-handle-tangle-list (&optional language tangle-file)
"Can be used as :override advice for `org-babel-tangle-collect-blocks'.
Handles lists of :tangle files."
(let ((counter 0) last-heading-pos blocks)
(org-babel-map-src-blocks (buffer-file-name)
(let ((current-heading-pos
(org-with-wide-buffer
(org-with-limited-levels (outline-previous-heading)))))
(if (eq last-heading-pos current-heading-pos) (cl-incf counter)
(setq counter 1)
(setq last-heading-pos current-heading-pos)))
(unless (org-in-commented-heading-p)
(let* ((info (org-babel-get-src-block-info))
(src-lang (nth 0 info))
(src-tfiles (cdr (assq :tangle (nth 2 info))))) ; Tobias: accept list for :tangle
(unless (consp src-tfiles) ; Tobias: unify handling of strings and lists for :tangle
(setq src-tfiles (list src-tfiles))) ; Tobias: unify handling
(dolist (src-tfile src-tfiles) ; Tobias: iterate over list
(unless (or (string= src-tfile "no")
(and tangle-file (not (equal tangle-file src-tfile)))
(and language (not (string= language src-lang))))
;; Add the spec for this block to blocks under its
;; language.
(let ((by-lang (assoc src-lang blocks))
(block (org-babel-tangle-single-block counter)))
(setcdr (assoc :tangle (nth 4 block)) src-tfile) ; Tobias:
(if by-lang (setcdr by-lang (cons block (cdr by-lang)))
(push (cons src-lang (list block)) blocks)))))))) ; Tobias: just ()
;; Ensure blocks are in the correct order.
(mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) blocks)))
(defun org-babel-tangle-single-block-handle-tangle-list (oldfun block-counter &optional only-this-block)
"Can be used as :around advice for `org-babel-tangle-single-block'.
If the :tangle header arg is a list of files. Handle all files"
(let* ((info (org-babel-get-src-block-info))
(params (nth 2 info))
(tfiles (cdr (assoc :tangle params))))
(message nil)
(if (null (and only-this-block (consp tfiles)))
(funcall oldfun block-counter only-this-block)
(cl-assert (listp tfiles) nil
":tangle only allows a tangle file name or a list of tangle file names")
(let ((ret (mapcar
(lambda (tfile)
(message tfile)
(sleep-for 1)
(let (old-get-info)
(cl-letf* (((symbol-function 'old-get-info) (symbol-function 'org-babel-get-src-block-info))
((symbol-function 'org-babel-get-src-block-info)
`(lambda (&rest get-info-args)
(let* ((info (apply 'old-get-info get-info-args))
(params (nth 2 info))
(tfile-cons (assoc :tangle params)))
(setcdr tfile-cons ,tfile)
info))))
(funcall oldfun block-counter only-this-block))))
tfiles)))
(if only-this-block
(list (cons (cl-caaar ret) (mapcar #'cadar ret)))
ret)))))
(advice-add 'org-babel-tangle-collect-blocks :override
#'org-babel-tangle-collect-blocks-handle-tangle-list)
(advice-add 'org-babel-tangle-single-block :around
#'org-babel-tangle-single-block-handle-tangle-list)
Append tangle
(defun org-babel-tangle-append (filename)
"Append source code block at point to its tangle file.
The command works like `org-babel-tangle' with prefix arg
but `delete-file' is ignored."
(interactive)
(cl-letf (((symbol-function 'delete-file) #'ignore))
(org-babel-tangle '(4) filename)))
Tangle file
This can be used to tangle one or more files to their output files Source on gitlab
(defun tangle-file(&rest files nokill)
"Tangle FILES or all files in the project."
(when (null files)
(setq files command-line-args-left))
(dolist (file files)
(with-current-buffer (find-file-noselect file)
(org-babel-tangle)
; (unless nokill (kill-buffer))
)))
Export file
This can be used to tangle one or more files to their output files Source on gitlab
(defun export-org-file-pdf(&rest files nokill)
"Export FILES or all files in the project."
(when (null files)
(setq files command-line-args-left))
(dolist (file files)
(with-current-buffer (find-file-noselect file)
(org-latex-export-to-pdf)
(kill-buffer))))
Crypto stuff
(require 'org-crypt)
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
(setq org-crypt-key "C86A9E7675295C62")
ox-export
Load ox-hugo
(with-eval-after-load 'ox
(require 'ox-hugo))
org-roam setup
(setq org-roam-subdir-alist '(("hack" . "hack")
("research-dsr" . "math")
("service" . "service")
("editorial" . "editorial")))
(setq org-roam-directory (file-truename (concat "~/org-roam/" (assoc-default (kde-current-activity-name) org-roam-subdir-alist))))
(setq org-roam-db-location
(file-truename (concat org-roam-directory "/org-roam.db")))
(setq org-roam-capture-templates
'(("d" "default" plain "%?" :target
(file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}
,#+startup: inlineimages
")
:unnarrowed t :kill-buffer tp)))
(org-roam-db-autosync-mode)
(setq org-roam-node-display-template
(concat "${title} "
(propertize "${tags:10}" 'face 'org-tag)))
(global-set-key (kbd "C-c n f") 'org-roam-node-find)
(global-set-key (kbd "C-c n c") 'org-roam-node-capture)
(global-set-key (kbd "C-c n l") 'org-roam-node-insert)
(global-set-key (kbd "C-c n n") 'org-roam-buffer-toggle)
(add-to-list 'display-buffer-alist
'("\\*org-roam\\*"
(display-buffer-in-direction)
(direction . bottom )
(window-width . fit-window-to-buffer)
(window-height . 0.40 )))
This snippet was taken from this thread, it allows to refile an org-headline to a org-roam node.
(defun org-roam-create-note-from-headline ()
"Create an Org-roam note from the current headline if it doesn't
exist without jumping to it"
(let* ((title (nth 4 (org-heading-components)))
;; Read in the name of the node, with the title filled in
;; TODO: How can I just use the title without user input?
(node (org-roam-node-read title)))
;; Skip the node if it already exists
(if (org-roam-node-file node)
(message "Skipping %s, node already exists" title)
;; Without this the subsequent kills seem to be grouped together, not
;; sure why
(kill-new "")
;; Cut the subtree from the original file
(org-cut-subtree)
;; Create the new capture file
(org-roam-capture- :node node)
;; Paste in the subtree
(org-paste-subtree)
;; Removing the heading from new node
(kill-whole-line)
;; Finalizing the capture will save and close the capture buffer
(org-capture-finalize nil)
;; Because we've deleted a subtree, we need the following line to make the
;; `org-map-entries' call continue from the right place
(setq org-map-continue-from
(org-element-property :begin (org-element-at-point))))))
(defun org-roam-create-note-from-headlines ()
(interactive)
(if (region-active-p)
;; `region-start-level' means we'll map over only headlines that are at
;; the same level as the first headline in the region. This may or may not
;; be what you want
(org-map-entries
'org-roam-create-note-from-headline t 'region-start-level)
;; If no region was selected, just create the note from the current headline
(org-roam-create-note-from-headline)))
export to ics
(defun wilder/push-subtree-to-nextcloud ()
(interactive)
(org-back-to-heading)
(org-map-entries #'wilder/push-heading-to-nextcloud nil 'tree))
(defun wilder/push-heading-to-nextcloud ()
(interactive)
(org-narrow-to-subtree)
(let ((org-icalendar-scheduled-summary-prefix "")
(org-icalendar-use-scheduled '(event-if-todo))
(org-icalendar-include-todo nil))
(shell-command (concat "~/scripts/push-to-nextcloud-cal.sh "
(org-icalendar-export-to-ics))))
(widen))
org-babel
I thought that i had no reason to use it, but let's see
(org-babel-lob-ingest "~/work/include/latexmkrc-base.org")
Make reftex work in org-edit
RefTeχ refuses to work when the buffer is not visiting a file. Here is a tentative workaround:
- extract the file we are tangling to
- use it as TeX-master
- hijack
buffer-file-nameto return something non-nil; I would rather hijack it locally (using cl-letf rather than advising the function since other things would be confused by the advice)
(defun hijack-buffer-file-name (orig-fun &rest args)
(if (not (boundp 'reftex-workaround-enabled))
(apply orig-fun args)
(cl-letf (((symbol-function 'buffer-file-name) (lambda (&optional buffer) "fragment.tex")))
(apply orig-fun args))))
(advice-add 'reftex-label :around
#'hijack-buffer-file-name)
(advice-add 'reftex-reference :around
#'hijack-buffer-file-name)
elisp
Paredit
(autoload 'enable-paredit-mode "paredit" "Turn on
pseudo-structural editing of Lisp code." t)
(add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
(add-hook 'scheme-mode-hook #'enable-paredit-mode)
(require 'highlight-parentheses)
(with-eval-after-load 'paredit
(define-key paredit-mode-map (kbd "<return>") #'paredit-open-round)
(define-key paredit-mode-map (kbd "C-<return>") #'paredit-wrap-round))
(defun hydra-paren/hl-paren-force-fix ()
;;; This is needed b/c hl-parent caches the point position and
;;; refuses to update if the point did not move
(let ((hl-paren-last-point -1))
(hl-paren-highlight)))
(defun hydra-paren/pre ()
(unless (boundp 'hydra-paren/hpm)
(highlight-parentheses-mode 1))
(setq hydra-paren/hpm t))
(defun hydra-paren/post ()
(highlight-parentheses-mode -1)
(makunbound 'hydra-paren/hpm))
(defhydra hydra-paren
(:pre hydra-paren/pre :post hydra-paren/post)
"paren slurp and barf"
("i" (progn
(paredit-forward-slurp-sexp)
(hydra-paren/hl-paren-force-fix)) "slurp forward")
("n" (progn
(paredit-forward-barf-sexp)
(hydra-paren/hl-paren-force-fix)) "barf forward")
("r" (progn
(paredit-backward-slurp-sexp)
(hydra-paren/hl-paren-force-fix)) "slurp backward")
("t" (progn
(paredit-backward-barf-sexp)
(hydra-paren/hl-paren-force-fix)) "barf backward"))
(global-set-key (kbd "C-c C-SPC") #'hydra-paren/body)
Replace last sexp
I use this a lot to evaluate (e.g.) quick computations in files
(defun replace-last-sexp ()
(interactive)
(let ((value (eval (preceding-sexp))))
(kill-sexp -1)
(insert (format "%S" value))))
(global-set-key (kbd "C-c C-x C-e") #'replace-last-sexp)
qml
Load qml-mode
(autoload 'qml-mode "qml-mode.el" t)
(add-to-list 'auto-mode-alist '("\\.qml\\'" . qml-mode))
Python
Silent offset warning
(setq python-indent-guess-indent-offset-verbose nil)
Dired
Do not show hidden files in dired
(setq dired-listing-switches "-l")
pdf-tools
Let us give pdf-tools a try
(pdf-tools-install)
(define-key pdf-view-mode-map (kbd "<mouse-12>")
'pdf-sync-backward-search-mouse)
(define-key pdf-view-mode-map (kbd "<mouse-11>")
'pdf-sync-backward-search-mouse)
(define-key pdf-view-mode-map (kbd "M-g M-g")
'pdf-view-goto-page)
(add-hook 'pdf-view-mode-hook #'auto-revert-mode)
(add-hook 'pdf-view-mode-hook (lambda () (margin-current-line-mode 0)))
C and C++
Hooks
(add-hook 'c-mode-hook
(lambda ()
(subword-mode)
(define-key c-mode-map (kbd "C-c C-c") 'make)))
(add-hook 'c++-mode-hook
(lambda ()
(subword-mode)
(define-key c++-mode-map (kbd "C-c C-c") 'make)))
TODO split — LaTeX
TODO
Setup reftex
(with-eval-after-load "reftex"
(setq reftex-cite-format "~\\cite{%l}"
reftex-plug-into-AUCTeX '(t t nil t t)
reftex-insert-label-flags '(t t)
reftex-label-alist
'(("thm" ?t "thm:" "~\\ref{%s}" thm (regexp "theorems?"))
("lem" ?l "lem:" "~\\ref{%s}" lem (regexp "lemma{ta}?"))
("prop" ?p "prp:" "~\\ref{%s}" prp (regexp "propositions?"))
("cor" ?c "cor:" "~\\ref{%s}" cor (regexp "corollary"))
("def" ?d "def:" "~\\ref{%s}" cor (regexp "defintions?")))))
;;; advice the auctex function to replace the
;;; lighter with somthing softer
(with-eval-after-load "auctex"
(defun cleanup-TeX-mode (&optional mode local reset)
"Advice for the base auctex MODE naming"
(setq mode-name (replace-regexp-in-string "LaTeX" "χ" mode-name t))
(setq mode-name (replace-regexp-in-string "/" "·" mode-name)))
(advice-add 'TeX-set-mode-name :after 'cleanup-TeX-mode))
Setup latex-mode
(eval-after-load 'latex
'(progn
(font-lock-add-keywords 'latex-mode
`((,(rx "$") 0 'font-latex-sedate-face t)) t)
(defface font-latex-special-comment-face '((t (:foreground "#2aa198"))) "Cyan")
(font-lock-add-keywords 'latex-mode '(("^% \\([^*].*\\)" 1 'font-latex-special-comment-face t)))
(define-key-alist LaTeX-mode-map
'(("M-S-SPC" . TeX-insert-braces)
("C-c C-v" . wilder/TeX-insert-reference)
("C-M-<return>" . wilder/TeX-insert-align-dwim)
;; unbind return - NOTE it is important to unbind
;; <return> and not RET. If we unbind RET then C-m won't work
;; either.
("<return>" . (lambda() (interactive) (insert "\\")))
("S-<return>" . (lambda() (interactive) (insert "|")))
("C-c C-." . LaTeX-mark-environment)
("C-c <return>" . LaTeX-environment)
("C-c C-e" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "_{" "}" r-begin r-end)))
("C-c C-u" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "^{" "}" r-begin r-end)))
("M-|" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "|" "|" r-begin r-end)))
("M-," . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter ", " ", " r-begin r-end)))
("M-\"" . (lambda (r-begin r-end)
(interactive "r")
(add-delimiter "“" "”" r-begin r-end)))
;; This is the rationale: C-M-SPC starts inline math C-M-RET starts display math
("C-M-SPC" . (lambda (r-begin r-end) (interactive "r") (add-delimiter "$" "$" r-begin r-end)))
("C-c C-d" . wilder/TeX-insert-todonote)
("C-M-d" . kill-sexp)
("C-M-i" . down-list) ;; -i stands for /in/
("C-M-o" . up-list) ;; -o stands for /out/
("<f4>" . ( lambda() (interactive) (message "Use C-c C-c")))
("C-c C-c" . ( lambda() (interactive) (compile "/home/jacopods/scripts/latex-mk")) )
("~" . wilder/TeX-replace-tilde)
("C-o" . wilder/open-line-and-indent)
;; Force moves around to be more “semantic”
("C-v" . goto-next-comment-line)
("M-v" . goto-previous-comment-line)
("C-x n c" . narrow-between-comments)
("C-S-v" . backward-paragraph)))
(dolist (ch '("=" "≠" ">" "<" "≥" "≤" "⇒" "∩" "∪" "∨" "∧" "×" "⊂" "⊃"))
(define-key LaTeX-mode-map (kbd ch) 'insert-char-with-padding))
;; Move around commands in the Right Way™
(modify-syntax-entry ?\\ "w" LaTeX-mode-syntax-table)
;; add fancy quotes to the syntax table
(modify-syntax-entry ?“ "(”" LaTeX-mode-syntax-table)
(modify-syntax-entry ?” ")“" LaTeX-mode-syntax-table)
(setq subword-forward-regexp "\\W*\\(\\([\\\\[:upper:]]*\\W?\\)[[:lower:][:digit:]]*\\)")
(setq subword-backward-regexp "\\(\\(\\W\\|[[:lower:][:digit:]]\\)\\([\\\\[:upper:]]+\\W*\\)\\|\\W\\w+\\)")))
Require
Load auctex, reftex and set up related hooks
(require 'tex-site)
(require 'reftex)
Set default TeX options
(setq-default TeX-master nil)
(setq TeX-auto-save t
TeX-parse-self t
TeX-insert-braces nil
TeX-view-program-selection '((output-pdf "PDF Tools"))
TeX-source-correlate-mode t
TeX-source-correlate-start-server t)
Appearance
No fontification for sub and superscripts
(setq font-latex-fontify-script nil)
Add unicode quotes to the quote-list
; (defvar font-latex-quote-list '(("``" "''") ("“" "”") ("<<" ">>" french) ("«" "»" french)) ;
Redefine fold ellipsis
(setq TeX-fold-ellipsis " …")
Default labels
(setq LaTeX-equation-label "e_"
LaTeX-section-label '( ("part" . "pp_")
("chapter" . "c_")
("section" . "s_")
("subsection" . "s_")
("subsubsection" . "s_"))
LaTeX-figure-label "f_")
Spell checking help
Do not spell-check inside the following commands. See [https://tex.stackexchange.com/questions/117204/skip-spelling-in-emacs-for-the-content-of-a-user-macro]
(setq ispell-tex-skip-alists
(list
(append
(car ispell-tex-skip-alists) ;tell ispell to ignore content of this:
'(("\\\\eqref" ispell-tex-arg-end)
("\\\\cite" ispell-tex-arg-end)
("\\\\author" ispell-tex-arg-end)
("\\\\address" ispell-tex-arg-end)
("\\\\include" ispell-tex-arg-end)
))
(cadr ispell-tex-skip-alists)))
Specialty functions
This function adds a pair of delimiters or surrounds the active region with the given delimiters. TODO it breaks when there is no mark
(defun add-delimiter (delim-begin delim-end r-begin r-end)
"Add the pair of delimiters given in delim at the ends of the
region if it is activated"
(interactive "cBegin delimiter: \ncEnd delimiter: \nr")
(if (region-active-p)
(progn
(save-excursion
(goto-char r-end)
(insert delim-end)
(goto-char r-begin)
(insert delim-begin)))
(progn
(save-excursion
(insert delim-end))
(insert delim-begin))))
The following re-implements TeX-insert-braces to work with
negative argument
(defun TeX-insert-braces (arg)
"Make a pair of braces around next ARG sexps and leave point inside.
No argument is equivalent to zero: just insert braces and leave point
between.
If there is an active region, ARG will be ignored, braces will be
inserted around the region, and point will be left after the
closing brace."
(interactive "P")
(if (TeX-active-mark)
(progn
(if (< (point) (mark))
(exchange-point-and-mark))
(insert TeX-grcl)
(save-excursion
(goto-char (mark))
(insert TeX-grop)))
(if (and arg (< arg 0))
(progn
(save-excursion
(backward-sexp (prefix-numeric-value (- 0 arg)))
(insert TeX-grop))
(insert TeX-grcl))
(insert TeX-grop)
(save-excursion
(if arg (forward-sexp (prefix-numeric-value arg)))
(insert TeX-grcl)))))
(defun TeX-back-insert-braces (arg)
(interactive "P")
(if arg (TeX-insert-braces (- 0 arg))
(insert TeX-grcl)))
This is a helper for Dynamic abbrev expansion
(defun dabbrev-expand-helper ()
(interactive)
(call-interactively 'dabbrev-expand))
This inserts a char adding some whitespace padding whenever necessary
(defun insert-char-with-padding (arg)
(interactive "*P")
(unless (string-match (string (preceding-char)) " \&")
(insert " "))
(self-insert-command (prefix-numeric-value arg))
(unless (char-equal (following-char) ?\s)
(insert " "))) ;; decide what to do with the point
This function opens a line and indents it
(defun wilder/open-line-and-indent (arg)
"This function opens a line using (open-line) and indents it"
(interactive "p")
(open-line arg)
(save-excursion (forward-char arg) (unless (eolp) (indent-according-to-mode))))
This inserts a plain reference (or a reference to an equation with prefix arg)
(defun wilder/TeX-insert-reference (arg)
(interactive "P")
(insert "~")
(if arg (TeX-insert-macro "eqref")
(TeX-insert-macro "ref")))
This inserts a citation
(defun wilder/TeX-insert-cite (arg)
(interactive "P")
(insert "~")
(TeX-insert-macro "cite"))
This inserts an align environment (or an align* with prefix arg)
(defun wilder/TeX-insert-align (arg)
(interactive "P")
(if arg
(LaTeX-insert-environment "align")
(LaTeX-insert-environment "align*"))
(indent-according-to-mode))
(defun wilder/TeX-insert-align-dwim (arg)
(interactive "P")
(if (texmathp)
(let ((why (car texmathp-why)))
(cond ((string-prefix-p "$" why) (wilder/TeX-promote-inline-math arg))
((string-equal "equation*" why) (LaTeX-modify-environment "align*"))
((string-equal "equation" why) (LaTeX-modify-environment "align"))
((string-equal "align*" why) (when arg (LaTeX-modify-environment "align")))))
(wilder/TeX-insert-align arg)))
This replaces horizontal space with a tilde
(defun wilder/TeX-replace-tilde (arg)
(interactive "P")
(delete-horizontal-space)
(insert "~"))
Insert a todonote at point or wrap the region in a todonote.
(defun wilder/TeX-insert-todonote (arg r-begin r-end)
(interactive "P\nr")
(if arg
(add-delimiter "{\\todo[inline]{" "}}" r-begin r-end)
(add-delimiter "{\\todo{" "}}" r-begin r-end)))
Promote inline math to an align
(defun wilder/char-punctuation-p (ch)
"returns t if the char is a punctuation"
(interactive)
(string-match (string ch) ".,:;"))
(defun wilder/TeX-promote-inline-math (arg)
(interactive "P")
(when (and (texmathp) (string-prefix-p "$" (car texmathp-why)))
(let* ((delim (car texmathp-why))
(pos (cdr texmathp-why))
(n (length delim)))
(goto-char pos)
(push-mark)
(delete-char n)
(search-forward delim)
(delete-backward-char n)
;; include punctuations in the align
(when (wilder/char-punctuation-p (char-after)) (forward-char))
(activate-mark)
(wilder/TeX-insert-align arg)
(pop-mark)
(pop-mark))))
Shadow the AucTeX version of this function
(defun LaTeX-insert-environment (environment &optional extra)
"Insert LaTeX ENVIRONMENT with optional argument EXTRA."
(let ((active-mark (and (TeX-active-mark) (not (eq (mark) (point)))))
prefix content-start env-start env-end)
(when (and active-mark (< (mark) (point))) (exchange-point-and-mark))
;; Compute the prefix.
(when (and LaTeX-insert-into-comments (TeX-in-commented-line))
(save-excursion
(beginning-of-line)
(looking-at
(concat "^\\([ \t]*" TeX-comment-start-regexp "+\\)+[ \t]*"))
(setq prefix (match-string 0))))
;; What to do with the line containing point.
(cond (;; if the line contains only whitespace, delete them
(save-excursion (beginning-of-line)
(looking-at (concat prefix "[ \t]*$")))
(delete-region (match-beginning 0) (match-end 0)))
;; otherwise, something is written on the line. We can be
;; at the beginning of the text
((TeX-looking-at-backward (concat "^" prefix "[ \t]*")
(line-beginning-position))
;;this morally opens a new line
(beginning-of-line)
(newline)
(beginning-of-line 0))
((bolp)
(delete-horizontal-space)
(newline)
(beginning-of-line 0))
((looking-at "[ \t]*$")
(delete-horizontal-space)
(newline)
(when prefix (insert prefix))
(beginning-of-line))
(t
(delete-horizontal-space)
(newline 2)
(when prefix (insert prefix))
(beginning-of-line 0)))
;; What to do with the line containing mark.
(when active-mark
(save-excursion
(goto-char (mark))
(cond ((save-excursion (beginning-of-line)
(or (looking-at (concat prefix "[ \t]*$"))
(looking-at "[ \t]*$")))
(delete-region (match-beginning 0) (match-end 0)))
((TeX-looking-at-backward (concat "^" prefix "[ \t]*")
(line-beginning-position))
(beginning-of-line)
(newline)
(beginning-of-line 0))
((looking-at "[ \t]*$")
(delete-horizontal-space)
(insert-before-markers "\n")
;(newline)
(when prefix (insert prefix)))
(t
(delete-horizontal-space)
(insert-before-markers "\n")
(newline)
(when prefix (insert prefix))))))
;; Now insert the environment.
(when prefix (insert prefix))
(setq env-start (point))
(insert TeX-esc "begin" TeX-grop environment TeX-grcl)
(indent-according-to-mode)
(when extra (insert extra))
(setq content-start (line-beginning-position 2))
(unless active-mark
(newline)
(when prefix (insert prefix))
(newline))
(when active-mark (goto-char (mark)))
(when prefix (insert prefix))
(insert TeX-esc "end" TeX-grop environment TeX-grcl)
(end-of-line 0)
(if active-mark
(progn
(or (assoc environment LaTeX-indent-environment-list)
(if auto-fill-function
;; Fill the region only when `auto-fill-mode' is active.
(LaTeX-fill-region content-start (line-beginning-position 2))))
(set-mark content-start))
(indent-according-to-mode))
(save-excursion (beginning-of-line 2) (indent-according-to-mode))
(TeX-math-input-method-off)
(setq env-end (save-excursion
(search-forward
(concat TeX-esc "end" TeX-grop
environment TeX-grcl))
(match-beginning 0)))
(run-hook-with-args 'LaTeX-after-insert-env-hooks
environment env-start env-end)))
Patch auctex
I don't particularly like the way that the prefix arguments works with font selection. The function below patches the annoying behavior, by defaulting to the default behavior if the prefix argument is 0
(defun wilder/TeX-font (replace what)
"Insert template for font change command.
If REPLACE is not nil, replace current font. WHAT determines the font
to use, as specified by `TeX-font-list'."
(interactive "*P\nc")
(TeX-update-style)
(let* ((entry (assoc what TeX-font-list))
(in-math (texmathp))
(before (nth 1 entry))
(after (nth 2 entry)))
(setq replace (or replace (eq t (nth 3 entry)) (eq t (nth 5 entry))))
(if (and in-math (stringp (nth 3 entry)))
(setq before (nth 3 entry)
after (nth 4 entry)))
(setq arg (prefix-numeric-value replace))
(cond
((null entry)
(let ((help (concat
"Font list: "
"KEY TEXTFONT MATHFONT\n\n"
(mapconcat 'TeX-describe-font-entry
TeX-font-list "\n"))))
(with-output-to-temp-buffer "*Help*"
(set-buffer "*Help*")
(insert help))))
((and replace (eq 0 arg))
(funcall TeX-font-replace-function before after))
((and replace (< arg 0) )
(progn
(save-excursion
(backward-sexp (- 0 arg))
(insert before))
(insert after)))
((and replace (> arg 0))
(insert before)
(save-excursion
(forward-sexp arg)
(insert after)))
((TeX-active-mark)
(save-excursion
(cond ((> (mark) (point))
(insert before)
(goto-char (mark))
(insert after))
(t
(insert after)
(goto-char (mark))
(insert before)))))
(t
(insert before)
(save-excursion
(insert after))))))
(advice-add 'TeX-font :override #'wilder/TeX-font)
The hook
(add-hook 'LaTeX-mode-hook
(lambda ()
(turn-on-reftex)
(setq comment-column 0)
(setq prettify-symbols-alist nil)
(add-to-list 'prettify-symbols-alist '(" ⊂ " . (? (Br . Bl) ? (Br . Bl) ?)))
(turn-on-auto-fill)
(subword-mode)
(TeX-fold-mode 1)
(outshine-mode)))
Compilation fixes
Load appropriate compliation filters for warnings
(load "latex-compile-filters.el")
Fix erroneous parsing of the LuaLaTeχ output "avail lists" as an error (works on ≥ emacs-27.1)
(add-to-list 'compilation-transform-file-match-alist '(" avail lists" nil))
Load bibretrieve
(byte-recompile-directory "~/.emacs.d/bibretrieve" 0)
(load "bibretrieve")
(setq bibretrieve-backends '(("msn" . 10)))
(require 'biblio)
Journal
Setup org-journal
(require 'org-journal)
(setq org-journal-dir "~/org/journal"
org-journal-file-type 'yearly
org-journal-file-format "%Y"
org-journal-encrypt-journal t)
Specialties
fix keyboard-quit
This is taken from Emacs Redux at this link
(defun er-keyboard-quit ()
"Smarter version of the built-in `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it."
(interactive)
(if (active-minibuffer-window)
(if (minibufferp)
(minibuffer-keyboard-quit)
(abort-recursive-edit))
(keyboard-quit)))
(global-set-key [remap keyboard-quit] #'er-keyboard-quit)
elfeed
(setq elfeed-feeds '("https://rss.arxiv.org/atom/math.ds"))
repeat-mode and smerge
Try to enable repeat-mode in smerge (see here
(defun repeatize (keymap)
"Add `repeat-mode' support to a KEYMAP."
(map-keymap
(lambda (_key cmd)
(when (symbolp cmd)
(put cmd 'repeat-map keymap)))
(symbol-value keymap)))
And enable!
(require 'smerge-mode)
(repeatize 'smerge-basic-map)
(repeat-mode)
try and fix issue with static-if
(require 'compat)
powerthesaurus
This is throwing an error (static-if)
(require 'powerthesaurus)
(global-set-key (kbd "C-<f18>") #'powerthesaurus-hydra/body)
beacon
beacon is a package that helps finding the point when switching
buffers. I like the idea but the aesthetics is a bit baroque. I
should do the same with flash-hline
flash-hline
This function is defined to help in training with new keybindings. It acts as a visual bell which flashes the current line. It is (arbitrarily) bound to F15 which is supposed to be triggered by some “illegal” key hit
(defun flash-hline ()
"Flash the current line to emph some mistake"
(interactive)
(let ((fg (face-foreground 'default))
(bg (face-background 'hl-line)))
(set-face-background 'hl-line fg)
(run-with-timer
0.1 nil (lambda ()
(set-face-background 'hl-line "#303030") ))))
(global-set-key (kbd "<f15>") 'flash-hline)
unfill-paragraph
This is authored by Stefan Monnier <foo at acm.org>. It is the opposite of fill-paragraph
(defun unfill-paragraph (&optional region)
"Takes a multi-line paragraph and makes it into a single line of text."
(interactive (progn (barf-if-buffer-read-only) '(t)))
(let ((fill-column (point-max))
;; This would override `fill-column' if it's an integer.
(emacs-lisp-docstring-fill-column t))
(fill-paragraph nil region)))
kill-word
dwim manage space after kill-word. This has been morally pasted from this reddit post. The original version does not allow for double spaces after a period.
(defun modi/just-one-space-post-kill-word (&rest _)
"Function to manage white space after `kill-word' operations.
1. If point is at the beginning of the line after possibly some white space,
remove that white space and re-indent that line.
2. If there is space before or after the point, ensure that there is only
one white space around the point.
3. Otherwise, do nothing.
During the whole operation do not change the point position with respect to the
surrounding white space.
abc| def ghi <-- point on the left of white space after 'abc'
abc| ghi <-- point still before white space after calling this function
abc |def ghi <-- point on the right of white space before 'def'
abc |ghi <-- point still after white space after calling this function."
(save-excursion ; maintain the initial position of the pt with respect to space
(cond ((looking-back "^ *") ; remove extra space at beginning of line
(just-one-space 0)
(indent-according-to-mode))
((looking-back "\\. *")
(just-one-space 2))
((or (looking-at " ")
(looking-back " ")) ; adjust space only if it exists
(just-one-space 1))
(t ; do nothing otherwise, includes case where the point is at EOL
))))
;; Delete extra horizontal white space after `kill-word' and `backward-kill-word'
(advice-add 'kill-word :after #'modi/just-one-space-post-kill-word)
word-count
Add word count in modeline; useful to prepare those pesky grant applications
(require 'word-count)
try fixing autorevert
This patches the inotify watch to follow symlinks. It is particulary useful for my setup where stuff is in symlinked directories (e.g. to tmpfs or to cloud providers)
(defun file-notify--add-watch-inotify (_file dir flags)
"Add a watch for FILE in DIR with FLAGS, using inotify."
(inotify-add-watch dir
(append
(and (memq 'change flags)
'(create delete delete-self modify move-self move))
(and (memq 'attribute-change flags)
'(attrib)))
#'file-notify--callback-inotify))
COMMENT gist
Use gist.el
(require 'gist)
Main packages
Magit
Load magit and introduce the keybinding
(require 'magit)
(global-set-key (kbd "C-x g") 'magit-status)
(global-set-key (kbd "C-x 5 g") (eval-with-new-frame (magit-status) (delete-other-windows)))
Customize the sections in magit-status by adding modules overview
and untracked files.
(magit-add-section-hook 'magit-status-sections-hook
#'magit-insert-modules-overview
#'magit-insert-unpushed-to-pushremote
:append)
(magit-add-section-hook 'magit-status-sections-hook
#'magit-insert-untracked-files
#'magit-insert-modules-overview
:append)
Enable magit-todos in magit status buffer
(require 'magit-todos)
(magit-todos-mode)
Enable orgit so that we can link to magit status buffer
(require 'orgit)
smart-tab
This package is a gem: it allows to make tab work dwim
(require 'smart-tab)
(global-smart-tab-mode 1)
Outshine
(defvar outline-minor-mode-prefix "\M-#")
(setq outshine-use-speed-commands t)
(with-eval-after-load 'outshine
(define-key outshine-mode-map (kbd "C-M-i") nil))
(add-hook 'sh-mode-hook 'outshine-mode)
(require 'outshine)
(require 'outorg)
Cook up some extra narrowing function
(defun goto-next-comment-line ()
(interactive)
(re-search-forward "^\\S<" nil 1)
(re-search-forward "^\\s<" nil 1)
(beginning-of-line)
)
(defun goto-previous-comment-line ()
(interactive)
(re-search-backward "^\\s<" nil 1 )
(re-search-backward "^\\S<" nil 1 )
(beginning-of-line)
(unless (bobp) (next-line))
)
(defun narrow-between-comments ()
(interactive)
(save-excursion
(next-line)
(goto-previous-comment-line)
(setq beginning (point)))
(save-excursion
(goto-next-comment-line)
(unless (bobp) (previous-line) (end-of-line))
(setq ending (point)))
(narrow-to-region beginning ending))
helm
(require 'helm)
(helm-mode 1)
(defun fix-helm-margins ()
(setq helm-left-margin-width left-margin-width)
(overlay-put helm-selection-overlay 'before-string
(propertize "." 'display
`((margin left-margin) ,(propertize "▶ " 'face 'default))))
)
(global-set-key-alist
'(("M-x" . helm-M-x)
("C-x f" . helm-find-files)
("C-x b" . helm-mini)
("M-y" . helm-show-kill-ring)
("C-c s" . helm-occur)))
(define-key helm-map (kbd "C-h") nil)
(define-key helm-find-files-map (kbd "C-h") nil)
(define-key helm-find-files-map (kbd "C-<backspace>") nil)
(define-key helm-read-file-map (kbd "C-<backspace>") nil)
(helm-define-key-with-subkeys helm-find-files-map (kbd "DEL") ?\d 'helm-ff-delete-char-backward
'(([C-c DEL] . helm-ff-run-toggle-auto-update))
nil 'helm-ff-delete-char-backward--exit-fn)
(add-hook 'helm-after-initialize-hook 'fix-helm-margins)
Enable “better” buffer handling on two-column layouts
(setq helm-always-two-windows nil
helm-split-window-inside-p t
helm-display-header-line nil)
Customize helm-mini adding bookmarks for files and dirs. This
is particularly useful to quickly open commonly accessed files
that I keep in the bookmarks. As an exercise, I wonder if we
could have per-server bookmarks.
(setq helm-mini-default-sources
'(helm-source-buffers-list
helm-source-recentf
helm-source-bookmark-files&dirs
helm-source-buffer-not-found))
Remove details from completions
(setq helm-completions-detailed nil)
Fix helm margins the bad way
(defun helm-display-mode-line (source &optional force)
"Set up mode-line and header-line for `helm-buffer'.
SOURCE is a Helm source object.
Optional argument FORCE forces redisplay of the Helm buffer's
mode and header lines."
(set (make-local-variable 'helm-mode-line-string)
(helm-interpret-value (or (and (listp source) ; Check if source is empty.
(assoc-default 'mode-line source))
(default-value 'helm-mode-line-string))
source))
(let ((follow (and (or (helm-follow-mode-p source)
(and helm-follow-mode-persistent
(member (assoc-default 'name source)
helm-source-names-using-follow)))
" (HF)"))
(marked (and helm-marked-candidates
(cl-loop with cur-name = (assoc-default 'name source)
for c in helm-marked-candidates
for name = (assoc-default 'name (car c))
when (string= name cur-name)
collect c))))
;; Setup mode-line.
(if helm-mode-line-string
(setq mode-line-format
`(:propertize
("╺═╸ " mode-line-buffer-identification " "
(:eval (format "L%-3d" (helm-candidate-number-at-point)))
,follow
" "
(:eval ,(and marked
(propertize
(format "M%d" (length marked))
'face 'helm-visible-mark)))
(:eval (when ,helm--mode-line-display-prefarg
(let ((arg (prefix-numeric-value
(or prefix-arg current-prefix-arg))))
(unless (= arg 1)
(propertize (format " [prefarg:%s]" arg)
'face 'helm-prefarg)))))
" "
(:eval (with-helm-buffer
(helm-show-candidate-number
(car-safe helm-mode-line-string))))
" " helm--mode-line-string-real " "
(:eval (make-string (window-width) ? )))
keymap (keymap (mode-line keymap
(mouse-1 . ignore)
(down-mouse-1 . ignore)
(drag-mouse-1 . ignore)
(mouse-2 . ignore)
(down-mouse-2 . ignore)
(drag-mouse-2 . ignore)
(mouse-3 . ignore)
(down-mouse-3 . ignore)
(drag-mouse-3 . ignore))))
helm--mode-line-string-real
(substitute-command-keys (if (listp helm-mode-line-string)
(cadr helm-mode-line-string)
helm-mode-line-string)))
(setq mode-line-format (default-value 'mode-line-format)))
;; Setup header-line.
(cond (helm-echo-input-in-header-line
(setq force t)
(helm--set-header-line))
(helm-display-header-line
(let ((hlstr (helm-interpret-value
(and (listp source)
(assoc-default 'header-line source))
source))
(endstr (make-string (window-width) ? )))
(setq header-line-format
(propertize (concat "··· " hlstr endstr)
'face 'helm-header))))))
(when force (force-mode-line-update)))
multiple-cursors
(require 'multiple-cursors)
(define-key mc/keymap (kbd "<return>") nil)
(global-set-key-alist
'(("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)
("C-c C-#" . mc/insert-numbers)
("C-c C-\\" . mc/mark-all-dwim)
("C-c <f17>" . mc/mark-pop)
("C-c <f18>" . mc/mark-pop)))
(defhydra cqql-multiple-cursors-hydra (:hint nil)
"
^Up^ ^Down^ ^Miscellaneous^
----------------------------------------------
[_p_] Next [_n_] Next [_l_] Edit lines
[_P_] Skip [_N_] Skip [_a_] Mark all
[_M-p_] Unmark [_M-n_] Unmark [_q_] Quit"
("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)
("p" mc/mark-previous-like-this)
("P" mc/skip-to-previous-like-this)
("M-p" mc/unmark-previous-like-this)
("q" nil))
(global-set-key (kbd "C-c \\") 'cqql-multiple-cursors-hydra/body)
avy
This packages allows fast navigation and action-ing It is probably a good idea to explore some smarter key-bindings
(require 'avy)
(avy-setup-default)
(global-set-key-alist
'(("C-c C-j" . avy-resume)
("C-SPC" . avy-goto-word-or-subword-1)
("M-s" . avy-goto-word-or-subword-1)
("M-g M-g" . avy-goto-line)
("M-g g" . avy-goto-line)))
(require 'ace-window)
(global-set-key (kbd "M-o") 'ace-window)
(setq aw-keys '(?f ?j ?d ?k ?s ?l ?a ?g ?h)
aw-scope 'global
aw-ignore-current t)
(require 'ace-link)
(ace-link-setup-default)
(setq avy-styles-alist nil
avy-background t)
(define-key org-mode-map (kbd "C-c M-o") 'ace-link-org)
(define-key org-agenda-mode-map (kbd "C-c M-o") 'ace-link-org)
expand-region
This is an excellent package, although I do not use it that much I should find a better binding
(require 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)
ERC
Enable erc dcc files transfer
(require 'erc-dcc)
nov.el
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
(setq nov-text-width 70
nov-variable-pitch nil)
(add-hook 'nov-mode-hook
#'(lambda () (setq-local show-trailing-whitespace nil)))
TODO Phase out Package.el
Load package.el
(require 'package)
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives
'("melpa-stable" . "http://stable.melpa.org/packages/") t)
Tidy-up
Save emacs-session files in appropriate directory
Save session files to the sessions directory so that they do not litter
the .emacs.d base directory.
(defun emacs-session-filename (session-id)
"Construct a filename to save the session in based on SESSION-ID.
If the directory ~/.emacs.d exists, we make a filename in there, otherwise
a file in the home directory."
(let ((basename (concat "sessions/session." session-id))
(emacs-dir user-emacs-directory))
(expand-file-name (if (file-directory-p emacs-dir)
(concat emacs-dir basename)
(concat "~/.emacs-" basename)))))
Moving to colemak
Set up all possible combinations of modifiers
(defun concat-recursive (b &optional a)
(if b
(append (concat-recursive (cdr b) (concat a (car b)))
(concat-recursive (cdr b) a))
(when a (list a))))
(setq modifier-combo (concat-recursive '("C-" "M-" "s-" "H-")))
Then list the swapped keys
(setq tarmak1-swap-alist '((?j . ?e)
(?e . ?k)
(?k . ?n)
(?n . ?j)))
(setq tarmak2-swap-alist '((?j . ?g)
(?g . ?t)
(?t . ?f)
(?f . ?e)
(?e . ?k)
(?k . ?n)
(?n . ?j)))
(setq tarmak3-swap-alist '((?j . ?r)
(?r . ?s)
(?s . ?d)
(?d . ?g)
(?g . ?t)
(?t . ?f)
(?f . ?e)
(?e . ?k)
(?k . ?n)
(?n . ?j)))
(setq tarmak-current-swap-alist tarmak3-swap-alist)
(defun set-key-translation-map (pairs)
(dolist (p pairs)
(define-key key-translation-map (kbd (downcase (car p))) (kbd (downcase (cdr p))))
(define-key key-translation-map (kbd (upcase (car p))) (kbd (upcase (cdr p))))))
(defun set-key-translation-map-caseless (pairs)
(dolist (p pairs)
(define-key key-translation-map (kbd (car p)) (kbd (cdr p)))))
(defun reset-key-translation-map ()
(dolist (c (mapcar 'cdr tarmak-current-swap-alist))
(define-key key-translation-map (kbd (downcase c)) nil)
(define-key key-translation-map (kbd (upcase c)) nil)))
(defun untarmak-modifiers ()
(interactive)
(set-key-translation-map-caseless
(mapcan (lambda (swap-pair)
(mapcar (lambda (mod-combo)
(cons (concat mod-combo
(string (car swap-pair)))
(concat mod-combo
(string (cdr swap-pair)))))
modifier-combo))
tarmak-current-swap-alist)))
(defun untarmak-key (char)
(let ((pair (rassoc char tarmak-current-swap-alist)))
(if pair (car pair) char)))
(defun tarmak-avy ()
(interactive)
(setq aw-keys '(?n ?t ?h ?d ?e ?s ?i ?r ?o ?a)
avy-keys '(?n ?t ?h ?d ?e ?s ?i ?r ?o ?a)))
(defun qwerty()
(interactive)
(reset-key-translation-map))
;; (untarmak-modifiers)
(tarmak-avy)
Finale
Fixup faces
This is really a workaround as I do not like either bold or italic.
It needs to be at the end of the file since it sets the face for
packages that have loaded in the meantime; yet it does not work
perfectly as some packages are still to be loaded (most notably
magit)
(set-face-bold 'bold nil)
(wilder/fixup-faces)
(with-eval-after-load "info" (wilder/fixup-faces) nil)
(with-eval-after-load "mu4e" (wilder/fixup-faces) nil)
(with-eval-after-load "mm-view" (wilder/fixup-faces) nil)
(with-eval-after-load 'gnus-cite (wilder/fixup-faces) nil)
(add-hook 'gnus-art-load-hook #'wilder/fixup-faces)