This configuration tangles out to multiple individual files using
org-babel-tangle
on save of this file.
To create the init files:
emacs --batch -l org readme.org -f org-babel-tangle
Windows specific configuration settings are saved to windows.el
and loaded on
the appropriate machines
The following projects and configurations have provided a source of inspiration for my setup:
- Crafted Emacs
- Nicolas Rougier particularly NANO though everything he touches is gold
- Org Modern
- Progfolio’s .emacs.d
Enable debug on error at startup to automatically get debugging with init.
;; -*- lexical-binding: t; -*-
(setq debug-on-error t)
(add-hook 'after-init-hook '(lambda () (setq debug-on-error nil)))
The EMACS ~~~ directory is not the same on windows as it is on Linux, I keep my git directory on
windows at c:/users/username/git
. Windows behaviour can sometimes be strange, and to avoid
unecessary headaches I avoid manually setting the emacs home directory.
(if (eq system-type 'windows-nt)
(setq git-directory (concat "c:/Users/" user-login-name "/git/"))
(setq git-directory (expand-file-name "~/git/")))
I keep my org directory in the same place
(setq org-directory (concat git-directory "org/"))
my emacs config also lives in the same place on all my machines
(setq ads/config-file (concat git-directory "emacs/readme.org"))
(lifted from progfolio’s init)
gc-cons-threshold
(800 KB) and gc-cons-percentage
(0.1) control when the Emacs garbage collector can kick in.
Temporarily turning these off during init should decrease startup time.
Resetting them afterward will ensure that normal operations don’t suffer from a large GC periods.
The following is a table shows values from popular Emacs configurations.
Distribution | gc-cons-threshold |
---|---|
Default | 800000 |
Doom | 16777216 |
Spacemacs | 100000000 |
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 1)
(defun +gc-after-focus-change ()
"Run GC when frame loses focus."
(run-with-idle-timer
5 nil
(lambda () (unless (frame-focus-state) (garbage-collect)))))
(defun +reset-init-values ()
(run-with-idle-timer
1 nil
(lambda ()
(setq ;;file-name-handler-alist default-file-name-handler-alist
gc-cons-percentage 0.1
gc-cons-threshold 100000000)
(message "gc-cons-threshold & file-name-handler-alist restored")
(when (boundp 'after-focus-change-function)
(add-function :after after-focus-change-function #'+gc-after-focus-change)))))
(add-hook 'after-init-hook '+reset-init-values)
Changes to the UI to inhibit unused elements be used should speed up start up times by a bit
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
(scroll-bar-mode -1) ;; Disable the visible scrollbar
(tool-bar-mode -1) ;; Disablet the toolbar
(tooltip-mode -1) ;; Disable tooltips
(menu-bar-mode -1) ;; Disable the menu bar
(setq inhibit-startup-screen t)
Remove server instruction clients, on closing an emacs frame when emacs server is running
(setq server-client-instructions nil)
Implicitly resizing the Emacs frame adds to init time. Fonts larger than the system default can cause frame resizing, which adds to startup time.
(setq frame-inhibit-implied-resize t)
Silence the bell
(setq ring-bell-function #'ignore
inhibit-startup-screen t)
On corporate machines I’ve been unable to get elpaca/straight working, for linux I plan to use the Nix emacs-overlay
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(setq use-package-always-ensure t)
(when (string= system-type 'gnu/linux)
(setq use-package-always-demand t))
Set these so emacs doesn’t litter files everywhere. At some point I might check out no-littering but this works for now
(setq backup-directory-alist `(("." . ,(expand-file-name "tmp/backups/" user-emacs-directory))))
(setq projectile-known-projects-file (expand-file-name "tmp/projectile-bookmarks.eld" user-emacs-directory)
lsp-session-file (expand-file-name "tmp/.lsp-session-v1" user-emacs-directory))
(provide 'early-init)
;;; early-init.el ends here
;(set-background-color "#000000") ;; don't blind me at startup
(setq
cursor-in-non-selected-windows nil
large-file-warning-threshold 100000000 ;; 100Mb
help-window-select t)
(blink-cursor-mode 0)
(fset 'yes-or-no-p 'y-or-n-p) ;; Replace yes/no prompts with y/n
(global-subword-mode 1) ;; Iterate through CamelCase words
(put 'downcase-region 'disabled nil) ;; Enable downcase-region
(put 'upcase-region 'disabled nil) ;; Enable upcase-region
(setq custom-file (concat user-emacs-directory "custom.el"))
(when (file-exists-p custom-file)
(load custom-file nil t))
(setq delete-trailing-lines nil)
(add-hook 'before-save-hook 'delete-trailing-whitespace)
I want to have line numbers whenever I’m in programming modes
(add-hook 'prog-mode-hook '(lambda () (display-line-numbers-mode 1)))
Automatically tangle my configuration files on save.
(setq org-babel-auto-tangle-file-list
(list ads/config-file))
(defun org-babel-auto-tangle-files ()
;; Automatically tangle files in ~org-babel-auto-tangle-file-list~ when one of them is saved
(when (member buffer-file-name org-babel-auto-tangle-file-list)
(org-babel-tangle-file buffer-file-name)))
Add hook to after save hook when on org mode
(add-hook 'org-mode-hook
(lambda () (add-hook 'after-save-hook 'org-babel-auto-tangle-files)))
(setq
mono "FiraCode Nerd Font"
sans "Cantarell"
serif "EtBembo")
;; Set Font sizes
(defvar default-font-size 140)
;; Set default font
(set-face-attribute 'default nil
:font mono
:family mono
:height default-font-size)
(set-face-attribute 'fixed-pitch nil
:font mono
:family mono
:height default-font-size)
(set-face-attribute 'variable-pitch nil
:font serif
:family serif
:height 1.1
:weight 'regular)
(customize-set-variable 'line-spacing 0.25)
Set frame padding to have some breathing room, making the dividers invisible is done with ads/modus-tweaks
(modify-all-frames-parameters
`((right-divider-width . 20)
(internal-border-width . 20)))
I used to maintain my own theme, but I wanted a change and modus themes are nice looking, I
particularly like the light modus-operandi
theme.
(use-package modus-themes
:demand t
:ensure t
:custom
(modus-themes-mixed-fonts t)
(modus-themes-bold-constructs t))
(setq modus-themes-to-toggle
'(modus-operandi modus-vivendi-tinted))
(setq modus-themes-headings
'((0 . (regular 1.75))
(1 . (regular 1.25))
(2 . (regular 1.20))
(3 . (regular 1.15))
(t . (regular 1.10))
))
(setq modus-operandi-palette-overrides
'((bg-mode-line-active bg-dim)
(bg-mode-line-inactive bg-main)))
(setq modus-vivendi-palette-overrides
'((bg-mode-line-active bg-dim)
(bg-mode-line-inactive bg-main)))
(setq modus-themes-common-palette-overrides
'((bg-prose-block-contents bg-main)
(bg-prose-block-delimiter bg-main)
(fg-heading-0 fg-main)
(fg-heading-1 fg-main)
(fg-heading-2 fg-main)
(fg-heading-3 fg-main)
(fg-heading-4 fg-main)
(fg-heading-5 fg-main)
(fg-heading-6 fg-main)
(fg-heading-7 fg-main)
(fg-heading-8 fg-main)))
(defun ads/modus-tweaks ()
(modus-themes-with-colors
(custom-set-faces
;; Modus
`(modus-themes-prose-code ((,c :foreground ,fg-alt)))
`(modus-themes-prose-verbatim ((,c :foreground nil)))
;; org mode
`(org-checkbox ((,c :foreground ,fg-main)))
`(org-table ((,c :foreground ,fg-main)))
`(org-document-info ((,c :foreground ,fg-main)))
`(prose-done ((,c :foreground ,fg-dim)))
`(org-done ((,c :foreground ,fg-dim)))
`(org-ellipsis ((,c :foreground ,bg-main)))
`(org-modern-label ((,c :height 0.7 :inherit fixed-pitch)))
`(org-scheduled ((,c :foreground ,fg-main)))
`(org-scheduled-today ((,c :foreground ,fg-main)))
`(org-scheduled-previously ((,c :weight normal :foreground ,fg-main)))
`(org-modern-date-active ((,c :inherit org-modern-label :background ,bg-blue-nuanced)))
`(org-modern-time-active ((,c :inherit org-modern-label :background ,bg-blue-subtle)))
`(org-modern-date-inactive ((,c :inherit org-modern-label :background ,bg-dim)))
`(org-modern-time-inactive ((,c :inherit org-modern-label :background ,bg-inactive)))
;; markdown mode
`(markdown-list-face ((,c :foreground ,fg-main)))
`(markdown-inline-code-face ((,c :foreground ,fg-main)))
`(markdown-code-face ((,c :background ,bg-dim )))
`(markdown-language-keyword-face ((,c :background ,bg-dim )))
;; line numbers
`(line-number ((,c :background ,bg-main :height 0.8)))
`(line-number-current-line ((,c :background ,bg-main)))
;; misc
`(cursor ((t :background ,red-intense)))
`(link ((t :foreground ,fg-main :underline ,fg-main)))
`(bookmark-face ((,c :foreground ,fg-dim :distant-foreground ,fg-dim)))
;; Make frame dividers invisible
`(fringe ((t :background ,bg-main :foreground ,bg-main)))
`(window-divider ((t :background ,bg-main :foreground ,bg-main)))
`(window-divider-first-pixel ((t :background ,bg-main :foreground ,bg-main)))
`(window-divider-last-pixel ((t :background ,bg-main :foreground ,bg-main)))
;; Add "padding" to the mode lines
`(mode-line ((,c :box (:line-width 3 :color ,bg-mode-line-active))))
`(mode-line-inactive ((,c :box (:line-width 3 :color ,bg-mode-line-inactive)))))))
(ads/modus-tweaks)
(load-theme 'modus-vivendi)
(add-hook 'modus-themes-after-load-theme-hook 'ads/modus-tweaks)
(add-hook 'after-init-hook 'ads/modus-tweaks)
general.el provides a more convenient method for binding keys in emacs. I use it to set all of my key bindings
(use-package general
:demand t
:ensure t
:config
(general-override-mode)
(general-auto-unbind-keys))
(general-define-key
:keymaps 'override
:states '(insert normal hybrid motion visual operator emacs)
:prefix "SPC"
:global-prefix "C-SPC")
(general-create-definer ads/leader-keys
:keymaps 'override
:states '(insert normal hybrid motion visual operator)
:wk-full-keys nil
:prefix "SPC"
:global-prefix "C-SPC")
(defun ads/keyboard-quit-dwim ()
"Do-What-I-Mean behaviour for a general `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.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
(interactive)
(cond
((region-active-p)
(keyboard-quit))
((derived-mode-p 'completion-list-mode)
(delete-completion-window))
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(t
(keyboard-quit))))
(general-define-key
:states '(normal hybrid motion visual operator emacs)
'"C-g" 'ads/keyboard-quit-dwim)
(ads/leader-keys
"C-r" 'replace-regexp
"C-j" 'jump-to-register)
(ads/leader-keys
"e" '(:ignore t :which-key "eval")
"eb" 'eval-buffer
"ed" 'eval-defun
"ee" 'eval-expression
"ep" 'pp-eval-last-sexp
"es" 'eval-last-sexp
)
(ads/leader-keys
"q" '(:ignore t :which-key "quit")
"qQ" 'save-buffers-kill-emacs
"qE" 'kill-emacs
)
(ads/leader-keys
"n" '(:ignore t :which-key "narrow")
"nn" 'narrow-to-region
"nd" 'narrow-to-defun
"ns" 'org-narrow-to-subtree
"np" 'narrow-to-page
"ne" 'org-narrow-to-element
"nb" 'org-narrow-to-block
"nw" 'widen
)
(ads/leader-keys
"j" '(:ignore t :which-key "frames")
"jQ" 'delete-frame
"jN" 'tear-off-window
"jR" 'set-frame-name
"jr" 'select-frame-by-name
"j=" 'balance-windows-area
"j_" 'split-window-vertically
"jh" 'evil-window-left
"jj" 'evil-window-down
"jk" 'evil-window-up
"jl" 'evil-window-right
"jH" 'evil-window-move-far-left
"jJ" 'evil-window-move-very-bottom
"jK" 'evil-window-move-very-top
"jL" 'evil-window-move-far-right
)
(ads/leader-keys
"k" '(:ignore t :wk "kill")
"kj" '(kill-buffer-and-window :which-key "kill-buffer-and-window")
"kk" '(kill-this-buffer :which-key "kill-this-buffer")
"kl"'(delete-window :wk "delete-window")
)
(ads/leader-keys
"c" '(:ignore t :which-key "config")
"cc" '((lambda () (interactive) (find-file ads/config-file))
:which-key "open config")
"cI" '((lambda () (interactive) (load-file user-init-file))
:which-key "load init"))
(ads/leader-keys
"t" '(:ignore t :which-key "toggles")
"tt" 'modus-themes-toggle
"tl" 'toggle-truncate-lines
"tb" 'display-battery-mode
"tm" 'display-time-mode
)
(use-package async
:config
(async-bytecomp-package-mode 1))
(customize-set-variable 'fill-column 80)
(add-hook 'text-mode-hook 'auto-fill-mode)
(use-package autorevert
:custom
(auto-revert-interval 0.01 "Instantaneously revert")
:config
(global-auto-revert-mode t))
BookmarkPlus [git] adds a lot of useful functionality to bookmarks, hosted on the EMACS wiki and not on MELPA
(use-package bookmark+
:vc (:url "https://github.com/emacsmirror/bookmark-plus"
:branch "master"))
(ads/leader-keys
"b" '(:ignore t :which-key "bookmark")
"bb" 'consult-bookmark
"bs" 'bookmark-set
"br" 'bookmark-rename)
CAPE (Completion At Point Extensions)
(use-package cape
:bind ("M-p" . cape-prefix-map)
:init
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-elisp-block))
Consult has quickly become one of my favorite emacs packages, it makes moving around emacs feel like magic
(use-package consult
:demand t
:config
(general-define-key
:states '(normal hybrid motion visual operator emacs)
'"M-y" 'consult-yank-pop
'"C-s" 'consult-line))
(ads/leader-keys
"C-SPC" 'consult-buffer
"SPC" 'consult-buffer
"C-;" 'consult-register-store
"r" 'consult-recent-file
"C-b" 'consult-bookmark)
(use-package corfu
:ensure t
:hook (after-init . global-corfu-mode)
:bind (:map corfu-map ("<tab>" . corfu-complete))
:config
(setq tab-always-indent 'complete)
(setq corfu-preview-current nil)
(setq corfu-min-width 20)
(setq corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)))
(require 'dired)
(add-hook 'dired-mode-hook 'dired-hide-details-mode)
(setq dired-kill-when-opening-new-dired-buffer t
delete-by-moving-to-trash t)
(general-define-key
:states '(normal motion emacs)
:keymaps 'dired-mode-map
"h" 'dired-up-directory
"l" 'dired-find-file)
(setq display-time-24hr-format t
display-time-day-and-date nil
display-time-default-load-average nil)
(display-time-mode)
(display-battery-mode)
(use-package doom-modeline
:demand t
:init (doom-modeline-mode 1)
:custom
(doom-modeline-height 24)
(doom-modeline-hud t)
(doom-modeline-icon t)
(doom-modeline-buffer-encoding nil)
(doom-modeline-percent-position nil)
(doom-modeline-time-icon nil)
:config
(setq
line-number-mode nil
column-number-mode nil))
(use-package evil
:demand t
:preface (setq evil-want-keybinding nil)
;; :after 'general
:hook (after-init . evil-mode)
:init
(setq evil-want-integration t
evil-want-keybinding nil
evil-want-C-u-scroll nil
evil-want-C-i-jump nil
evil-want-C-w-delete nil
evil-complete-all-buffers nil
)
:config
(general-define-key :states 'insert "C-g" 'evil-normal-state)
;; Use visual line motions even outside of visual-line mode buffers
(evil-global-set-key 'motion "j" 'evil-next-visual-line)
(evil-global-set-key 'motion "k" 'evil-previous-visual-line)
;; set back normal mouse behaviour
(define-key evil-motion-state-map [down-mouse-1] nil)
(add-hook 'after-save-hook 'evil-normal-state)
;; unbind q for macros
(define-key evil-normal-state-map (kbd "q") 'nil)
(define-key evil-normal-state-map (kbd "Q") 'nil))
(general-define-key
:states '(normal insert)
"C-w C-h" 'evil-window-left
"C-w C-j" 'evil-window-down
"C-w C-k" 'evil-window-up
"C-w C-l" 'evil-window-right)
Show match counts in modeline
(use-package evil-anzu
:after (evil)
:config
(global-anzu-mode))
A collection of evil bindings not fully set in the default package
(use-package evil-collection
:after (evil)
:custom
(evil-collection-calendar-setup-want-org-bindings t)
(evil-collection-setup-minibuffer t)
:config
(evil-collection-init))
Quickly create links to files and commits
(use-package git-link
:config
(ads/leader-keys
"gf" 'git-link
"gF" 'git-link-dispatch))
(use-package helpful
:demand t
)
(general-define-key
:states '(normal insert)
"C-h C-v" 'describe-variable
"C-h C-f" 'describe-function
"C-h C-b" 'describe-bindings
"C-h C-c" 'describe-key-briefly
"C-h C-k" 'describe-key
"C-h C-e" 'view-echo-area-messages
"C-h C-j" 'describe-face)
Sometimes when I’m writing code I want to be able to directly insert the value of a variable in to the buffer I am editing. There’s probably a way to do this if I look through the manual closely but this works for now.
(defun insert-any-variable-value (var)
"Insert the value of any variable VAR at point."
(interactive
(list (intern (completing-read
"Insert variable value: "
(let (vars)
(mapatoms (lambda (sym)
(when (boundp sym)
(push (symbol-name sym) vars))))
vars)))))
(insert (format "%S" (symbol-value var))))
(ads/leader-keys
"C-v" 'insert-any-variable-value)
(use-package magit
:config
(transient-bind-q-to-quit)
(defun ads/git-lazy ()
(interactive)
(save-buffer)
(magit-stage-buffer-file)
(magit-commit-create))
(defun ads/git-amend ()
(interactive)
(save-buffer)
(magit-stage-buffer-file)
(magit-commit-amend "--no-edit"))
(ads/leader-keys
"g" '(:ignore t :wk "git")
"gd" 'magit-dispatch
"gg" 'magit-status
"gk" 'magit-commit
"gl" 'ads/git-lazy
"go" 'ads/git-amend
"gp" 'magit-push
"gP" 'vc-push
"gs" 'magit-stage-buffer-file
"gS" 'magit-stage
"gu" 'magit-unstage-buffer-file
"gU" 'magit-unstage))
(use-package marginalia
:ensure t
:demand t
:hook (after-init . marginalia-mode))
(use-package markdown-mode
:custom
(markdown-fontify-code-blocks-natively)
(markdown-fontify-code-block-default-mode)
(markdown-list-item-bullets '("•"))
(markdown-fontify-code-blocks-natively "t")
:config
(add-hook 'markdown-mode-hook 'variable-pitch-mode)
(add-hook 'markdown-mode-hook 'markdown-display-inline-images)
(add-hook 'markdown-view-mode-hook 'read-only-mode))
(ads/leader-keys
:keymaps 'markdown-mode-map
"oo" 'consult-outline
"mm" 'markdown-view-mode)
(ads/leader-keys
:keymaps 'markdown-view-mode-map
"mm" 'markdown-mode)
Remember run nerd-icons-install-fonts
to get the font files. Then
restart Emacs to see the effect.
(use-package nerd-icons
:ensure t)
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
(use-package nerd-icons-corfu
:ensure t
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package nerd-icons-dired
:ensure t
:hook
(dired-mode . nerd-icons-dired-mode))
(use-package nix-mode
:config
(global-nix-prettify-mode))
Major mode for reading EPUB files in Emacs
(use-package nov
:custom
(nov-text-width 80)
:config
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))
(use-package orderless
:ensure t
:custom
(completion-styles '(orderless basic))
(completion-category-overrides '((file (styles basic partial-completion)))))
(use-package org
:custom
;; (org-directory "~/org") ;; org directory set in early init
(org-ellipsis " ·")
(org-log-done 'time)
(org-log-into-drawer t)
(org-pretty-entities t)
(org-pretty-entities-include-sub-superscripts nil)
(org-hidden-keywords '(title))
(org-hide-emphasis-markers t)
(org-image-actual-width 0.75)
(org-startup-with-inline-images t)
(org-fontify-whole-heading-line t)
(org-fontify-done-headline t)
(org-fontify-quote-and-verse-blocks t)
(org-cycle-separator-lines 0)
(org-id-link-to-org-use-id t)
(org-blank-before-new-entry '((heading . nil) (plain-list-item . nil)))
(org-todo-keywords
'((sequence "TODO(t)" "|" "DONE(d)")
(sequence "PLAN(p)" "NEXT(n)" "STOP(s!)" "HOLD(h!)"
"|" "Done(d!)" "CANC(k!)")))
(org-tag-alist
'(("Article" . ?r)
("Book" . ?b)
("Project" . ?p)))
:config
(add-hook 'org-mode-hook 'variable-pitch-mode))
(general-define-key
:states '(normal) :keymaps 'org-mode-map
(kbd "<tab>") 'org-cycle
(kbd "<backtab>") 'org-shifttab)
(general-define-key
:states '(motion) :keymaps 'org-mode-map
(kbd "RET") 'org-open-at-point)
(defun ads/consult-org-outline ()
"Widen buffer, consult outline then narrow to subtree"
(interactive)
(widen)
(consult-outline)
(org-narrow-to-subtree))
(defun ads/org-scratch ()
"Open ~/scratch.org"
(interactive)
(if (eq system-type 'windows-nt)
(find-file (concat "c:/users/" user-login-name "/scratch.org"))
(find-file "~/scratch.org")))
(ads/leader-keys
"C-s" 'ads/org-scratch)
(ads/leader-keys
:major-modes '(org-mode)
:keymaps '(org-mode-map)
"od" 'org-id-get-create
"oo" 'ads/consult-org-outline
"of" 'consult-outline
"oh" 'consult-org-heading)
(require 'org-agenda)
;; (evil-make-overriding-map org-agenda-mode-map)
(setq org-agenda-window-setup 'current-window
org-agenda-span 'day
org-agenda-block-separator ""
org-agenda-restore-windows-after-quit t
org-agenda-persistent-filter t
org-agenda-scheduled-leaders '(" " "%2dd")
)
(setq org-agenda-prefix-format
'((agenda . " %-20 c%?-12t% s")
(todo . " %-20 c")
(tags . " %-20 c")
(search . " %-20 c")))
(ads/leader-keys
"oa" 'org-agenda)
Largely taken from evil-org-agenda, but since there’s only one mode applicable I don’t feel the need to use lots of the evil bindings
(general-define-key
:keymaps 'org-agenda-mode-map
"j" 'org-agenda-next-line
"k" 'org-agenda-previous-line
"h" 'org-agenda-earlier
"l" 'org-agenda-later
"H" 'org-agenda-do-date-earlier
"L" 'org-agenda-do-date-later
"S" 'org-agenda-schedule
"m" 'org-agenda-bulk-toggle
"x" 'org-agenda-bulk-action
"a" 'org-agenda-add-note
"u" 'org-agenda-undo
";" 'org-agenda-set-tags
;; refresh
"gr" 'org-agenda-redo
"gR" 'org-agenda-redo-all
;; delete
"dd" 'org-agenda-kill
"dA" 'org-agenda-archive
;; filter
"sc" 'org-agenda-filter-by-category
"sr" 'org-agenda-filter-by-regexp
"se" 'org-agenda-filter-by-effort
"st" 'org-agenda-filter-by-tag
"s^" 'org-agenda-filter-by-top-headline
"ss" 'org-agenda-limit-interactively
"S" 'org-agenda-filter-remove-all
"C-w C-h" 'evil-window-left
"C-w C-j" 'evil-window-down
"C-w C-k" 'evil-window-up
"C-w C-l" 'evil-window-right)
(use-package org-appear
:custom
(org-appear-autolinks t)
(org-appear-autoentities t)
(org-appear-autosubmarkers t)
(org-appear-autokeywords t)
:config
(add-hook 'org-mode-hook 'org-appear-mode)
(add-hook 'evil-insert-state-exit-hook
(lambda ()
(setq org-appear-delay 2)))
(add-hook 'evil-insert-state-entry-hook
(lambda ()
(setq org-appear-delay .3))))
(require 'org-tempo)
(require 'ob-tangle)
(customize-set-variable 'org-src-window-setup 'current-window)
(customize-set-variable 'org-src-preserve-indentation t)
(customize-set-variable 'org-edit-src-content-indentation 0)
(setq org-confirm-babel-evaluate nil)
<<org-babel-config>>
(dolist
(template
'(
("el" . "src emacs-lisp")
("py" . "src python")
("sh" . "src shell")
("rs" . "src rust")
("html" . "src html")
("css" . "src css")
("cc" . "src C")
("cpp" . "src C++")
("cs" . "src C#")
("yaml" . "src yaml")
("toml" . "src toml")
("js" . "src javascript")
("jo" . "src json")
("ja" . "src java")
("sql" . "src sql")
))
(add-to-list 'org-structure-template-alist template))
(with-eval-after-load 'org
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t))))
(setq org-confirm-babel-evaluate nil)
(setq org-babel-default-header-args:python
'((:results . "output")
))
Capture all of my notes and tasks to an inbox where they are then reviewed reviewed from there
(setq ads/inbox-file (concat org-directory "inbox.org"))
(setq org-default-notes-file ads/inbox-file)
(setq org-capture-templates
'(
<<capture-inbox>>
<<capture-task>>
<<capture-note>>
))
(defun ads/inbox ()
"Open ads/inbox-file"
(interactive)
(find-file ads/inbox-file))
(defun ads/quick-note ()
"take a note using the capture-note template"
(interactive)
(org-capture nil "n"))
(ads/leader-keys
"oc" 'org-capture
"oi" 'ads/inbox
"C-a" 'ads/quick-note)
("i" "inbox" plain
(file ads/inbox-file)
"* %^{}
:CREATED: %U
"
:immediate-finish)
("t" "todo" plain
(file ads/inbox-file)
"* TODO %?
SCHEDULED: %t
")
("n" "note" plain
(file ads/inbox-file)
"* %^{HEADING}
:CREATED: %U
%?
")
(use-package org-modern
:after (org)
:custom
(org-modern-fold-stars
'(("▸ " . "▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")
(" ▸ " . " ▾ ")))
(org-modern-checkbox
'((88 . " ")
(45 . " ")
(32 . " ")))
:config
(global-org-modern-mode)
)
Org-Roam is an org mode implementation of Roam Research’s idea of a zettelkasten like system with links between individual notes. It is extremely powerful but has a bit of a learning cuve to learn your way around and how best to work with it.
Given that I use org-roam for personal and work at the same time on work computers I have ~~/org/personal~ and ~~/org/work~
(use-package org-roam
:init
(setq org-roam-v2-ack t)
:custom
(org-roam-directory org-directory)
(org-roam-completion-everywhere t)
:config
(org-roam-db-autosync-mode)
(org-roam-setup)
(ads/leader-keys
"f" '(:ignore t :wk "roam")
"ft" 'org-roam-buffer-toggle
"ff" 'org-roam-node-find
"fi" 'org-roam-node-insert
"fa" 'org-roam-alias-add
"f;" 'org-roam-tag-add
"f:" 'org-roam-tag-remove
"fD" '((lambda () (interactive)
(org-roam-db-sync)
(ads/org-agenda-files-update))
:wk "db sync & agenda")
;; "fd" (:ignore t :wk "roam dailies")
"fd" 'org-roam-dailies-map
))
I generate my org agenda files based off of an :agenda: tag in each org roam
file, in addition to an inbox.org
file. Credit to d12frosted and
magnus.therning for the inspiration.
(add-to-list 'org-tags-exclude-from-inheritance "agenda")
(defun ads/org-todo-p ()
"Return non-nil if current buffer has any todo entry.
TODO entries marked as done are ignored, meaning the this
function returns nil if current buffer contains only completed
tasks."
(org-element-map ; (2)
(org-element-parse-buffer 'headline) ; (1)
'headline
(lambda (h)
(eq (org-element-property :todo-type h)
'todo))
nil 'first-match)) ; (3))
While most of roam agenda section is unchaged, a few org-roam functions have
been deprecated since writing and org-roam-tag-add
has been added which
simplifies the task of adding a tag
(defun ads/org-update-agenda-tag ()
"Add :agenda: tag to the current org-roam buffer"
(when (and (not (active-minibuffer-window))
(org-roam-file-p buffer-file-name))
(save-excursion
(goto-char (point-min))
(if (ads/org-todo-p)
(org-roam-tag-add '("agenda"))
(org-roam-tag-remove '("agenda"))))))
(add-hook 'before-save-hook 'ads/org-update-agenda-tag)
(defun ads/org-roam-agenda-files ()
"return a list of files containing the :agenda: tag"
(seq-uniq
(seq-map
#'car
(org-roam-db-query
[:select [nodes:file]
:from tags
:left-join nodes
:on (= tags:node-id nodes:id)
:where (like tag (quote "%\"agenda\"%"))]))))
(defun ads/org-agenda-files-update (&optional arg)
"Update org agenda files list"
(setq org-agenda-files (ads/org-roam-agenda-files))
(add-to-list 'org-agenda-files ads/inbox-file)
(message "Agenda files updated from roam tags"))
(add-hook 'after-init-hook 'ads/org-agenda-files-update)
(advice-add 'org-agenda :before #'ads/org-agenda-files-update)
(advice-add 'org-todo-list :before #'ads/org-agenda-files-update)
Categories in org agenda also get messed up, often I go back and fix them if I’m going to be seeing it
(defun ads/org-node-name-to-category ()
(interactive)
(when (org-roam-file-p)
(save-excursion
(goto-char (point-min))
(org-roam-property-add
"CATEGORY"
(org-roam-node-title (org-roam-node-at-point))))))
(ads/leader-keys "fc" 'ads/org-node-name-to-category)
While writing and not wanting to create a rabbit hole for myself I want to make a node immediately and not dive further into it. Credit to Umar Ahmad
(defun org-roam-node-insert-immediate (arg &rest args)
(interactive "P")
(let ((args (cons arg args))
(org-roam-capture-templates (list (append (car org-roam-capture-templates)
'(:immediate-finish t)))))
(apply #'org-roam-node-insert args)))
(ads/leader-keys "fI" 'org-roam-node-insert-immediate)
I want to be able to see where my files are and what tags they have when I search for nodes and files
(cl-defmethod org-roam-node-type ((node org-roam-node))
"Return the TYPE of NODE."
(condition-case nil
(file-name-nondirectory
(directory-file-name
(file-name-directory
(file-relative-name (org-roam-node-file node) org-roam-directory))))
(error "")))
(setq org-roam-node-display-template
(concat (propertize "${title:50}" 'face 'org-verbatim)
(propertize " ${tags:30}" 'face 'org-tag)
(propertize " ${type:15}" 'face 'org-roam-dim)
))
Org-Roam-UI is a frontend for exploring and interacting with your org-roam notes.
(use-package org-roam-ui
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
org-tidy will automatically hide property drawers
(use-package org-tidy
:ensure t
:custom
(org-tidy-properties-style 'invisible)
:hook
(org-mode . org-tidy-mode))
(ads/leader-keys
:keymaps 'org-mode-map
"ot" 'org-tidy-untidy-buffer
"oT" 'org-tidy-toggle)
Projectile is a popular emacs package used to manage projects
(use-package projectile
:custom
(projectile-sort-order 'recently-active)
:config
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(projectile-mode)
(autoload 'projectile-project-root "projectile")
(setq consult-project-function (lambda (_) (projectile-project-root)))
(add-to-list 'projectile-globally-ignored-directories "*target")
(add-to-list 'projectile-globally-ignored-directories "*venv"))
(ads/leader-keys
"p" '(:ignore t :wk "projects")
"pf" 'projectile-find-file-dwim
"pp" 'consult-project-buffer
"pP" 'projectile-switch-project
"pj" 'projectile-next-project-buffer
"pk" 'projectile-previous-project-buffer)
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
(use-package rainbow-mode
:commands (rainbow-mode))
Some directories I want to files to be opened in read-only mode because they are usually used for reference
(defcustom read-only-directories '( )
"list of directories or files that will be opened in read only mode")
(defun find-file-read-only-directories ()
"""
start buffer in read only mode if file in a child directory
of in any of the directores defined in read-only-directories
"""
(dolist (read-only-directory read-only-directories)
(when (string-search read-only-directory buffer-file-name)
(read-only-mode))))
(add-hook 'find-file-hook 'find-file-read-only-directories)
(use-package recentf
:custom
(recentf-max-menu-items 1000 "Offer more recent files in menu")
(recentf-max-saved-items 1000 "Save more recent files")
:config
(recentf-mode)
)
Rust-mode is a minimal package that probides Rust support and bindings supported by the rust-lang team, alternatively Rustic offers a more fully featured experience.
(use-package rust-mode
:init
(setq rust-mode-treesitter-derive t)
:config
(setq rust-format-on-save t))
(use-package savehist
:config
(savehist-mode 1))
(use-package sudo-edit)
(use-package treemacs
:config
(ads/leader-keys
"C-f" 'treemacs))
(use-package treemacs-evil
:after treemacs evil)
(use-package treemacs-magit
:after treemacs magit)
(use-package treemacs-projectile
:after treemacs projectile)
(use-package treemacs-nerd-icons
:after treemacs
:config
(treemacs-load-theme "nerd-icons"))
Scroll Emacs like Lightining.
ultra-scroll provides a better version of pixel-scroll-prescision-mode
which
works much smoother on both Windows and Linux.
(use-package ultra-scroll
:vc (ultra-scroll
:url "https://github.com/jdtsmith/ultra-scroll"
:main-file "ultra-scroll.el"
:branch "main"
:rev :newest)
:init
(setq scroll-conservatively 101
scroll-margin 0)
:config
(ultra-scroll-mode 1))
(use-package vertico
:demand t
:hook (after-init . vertico-mode)
:config
(general-define-key
:states '(insert)
:keymaps 'vertico-map
"C-j" 'vertico-next
"C-k" 'vertico-previous))
which-key
is is included in EMACS 30
(use-package which-key
:demand t
:init
(setq which-key-enable-extended-define-key t)
:config
(which-key-mode)
:custom
(which-key-side-window-location 'bottom)
(which-key-sort-order 'which-key-key-order-alpha)
(which-key-side-window-max-width 0.33)
(which-key-idle-delay 0.3))
Load windows only configuration
(when (eq system-type 'windows-nt)
(load-file (concat user-emacs-directory "ms-windows.el")))
(when (eq system-type 'gnu/linux)
(load-file (concat user-emacs-directory "linux.el")))
Load when on appropriate system:
(when (string-equal-ignore-case system-name "HYBD-QUFXNBP9Y9")
(load-file (concat git-directory "emacs-work/work.el")))
(when (string-equal-ignore-case system-name "ganymede")
(load-file (concat git-directory "windows-config/ganymede.el")))
To set emacs daemon to start on windows at login:
- win+r
shell:startup
- make shortcut to
runemacs.exe
- modify shortcut and add –daemon
Or open task scheduler and add a task to execute runemacs.exe --daemon flag
(set-message-beep 'silent)
(setq win/.emacs.d (concat "C:\\Users\\" user-login-name "\\AppData\\Roaming\\.emacs.d\\"))
Windows doesn’t allow symlinks unless you are an admin which is a pain. In
order to keep the init files working properly copy the config files to
C:\Users\username\AppData\Roaming\.emacs.d\
after tangle
(defun win/copy-config-files-to-.emacs.d ()
(when (string-equal-ignore-case buffer-file-name ads/config-file)
(dolist (filename
'("early-init.el"
"init.el"
"ms-windows.el"))
(let ((target-filename (concat win/.emacs.d filename)))
(delete-file target-filename)
(copy-file filename target-filename)))
(message "Copied config files to win/.emacs.d")))
(add-hook 'org-babel-tangle-finished-hook 'win/copy-config-files-to-.emacs.d)
(ads/leader-keys
"cW" '((lambda () (interactive) (find-file win/.emacs.d))
:wk "Dired .emacs.d"))
I use AHK a lot to make the experience of using windows less painful
(use-package ahk-mode
:ensure t
:bind (:map ahk-mode-map
("C-c C-c" . ahk-run-script)
("C-c C-k" . nil)
)
)
(defun ahk-launch-window-spy ()
(interactive)
(w32-shell-execute 1 "C:/Users/adanaos/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/AutoHotkey Window Spy.lnk"))
;; win/theme
;; 0 - dark
;; 1 - light
(setq win/theme "0")
(add-to-list 'display-buffer-alist
(cons "win/theme-toggle" (cons #'display-buffer-no-window nil)))
(defun win/theme-align-with-emacs ()
;;check if light or dark theme in emacs
(if (string-search "vivendi"
(symbol-name (modus-themes--current-theme)))
(setq win/theme "0")
(setq win/theme "1"))
(async-shell-command
(concat
"powershell New-ItemProperty -Path HKCU:/SOFTWARE/Microsoft/Windows/CurrentVersion/Themes/Personalize -Name AppsUseLightTheme -Value "
win/theme
" -Type Dword -Force")
"win/theme-toggle"
))
(add-hook 'modus-themes-after-load-theme-hook 'win/theme-align-with-emacs)
Some things only work in edge on my work computer
(defun win/browse-url-edge (url)
(shell-command (concat "start msedge " url)))
Some the files I work with don’t have consistent line endings DOS or UNIX
(defun win/hide-dos-eol ()
"Do not show ^M in files containing mixed UNIX and DOS line endings."
(interactive)
(setq buffer-display-table (make-display-table))
(aset buffer-display-table ?\^M []))
Often emacs and windows don’t always play nice together and causes emacs to lock
up, this executes a .bat
script in a new window, which seems to fix the problems
I have.
This version keeps it as a sub process, if you need to fix that you can save the
start-process
to a variable and (set-process-query-on-exit-flag
start-process-variable nil)
(defun win/cmd-exec-bat-new-window (input-str)
(let ((cmd-str (concat "start cmd /k " input-str)))
(start-process "cmd" nil "cmd.exe" "/C" cmd-str)))
(defun org-attatch-open-win-explorer ()
(interactive)
(w32-shell-execute 1 (org-attach-dir-get-create)))
(defun ads/dired-win-default ()
(interactive)
(let ((filename
(dired-replace-in-string "/" "\\" (dired-get-filename))))
(w32-shell-execute 1 filename)))
(general-define-key
:keymaps 'dired-mode-map
"<tab>" 'ads/dired-win-default)
(provide 'ms-windows.el)
jump to my nix config files
(ads/leader-keys
"cn" '((lambda () (interactive) (find-file "/etc/nixos/configuration.nix"))
:which-key "ni config"))