Skip to content

andanao/emacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Literate Configuration

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:

early-init

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)))

Set directory variables

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"))

Garbage Collection

(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.

Distributiongc-cons-threshold
Default800000
Doom16777216
Spacemacs100000000
(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)

UI Changes

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)

Package Usage

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))

Backup files

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

(provide 'early-init)
;;; early-init.el ends here

Emacs Settings

;(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)))

auto tangle files

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)))

Theme

fonts

(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)

frames

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)))

modus-themes

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)))

modus-tweaks

(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

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)

eval e

(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
  )

quit q

(ads/leader-keys
  "q" '(:ignore t :which-key "quit")
  "qQ" 'save-buffers-kill-emacs
  "qE" 'kill-emacs
  )

narrow n

(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
  )

windows, buffers, frames j

(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
  )

kill k

(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")
  )

config c

(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"))

Toggles t

(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
    )

Packages

async

(use-package async
  :config
  (async-bytecomp-package-mode 1))

auto-fill

(customize-set-variable 'fill-column 80)
(add-hook 'text-mode-hook 'auto-fill-mode)

auto-revert

(use-package autorevert
  :custom
  (auto-revert-interval 0.01 "Instantaneously revert")
  :config
  (global-auto-revert-mode t))

bookmark+

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

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

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)

corfu

(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)))

Dired

(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)

display-time-mode

(setq display-time-24hr-format t
      display-time-day-and-date nil
      display-time-default-load-average nil)
(display-time-mode)

display-battery

(display-battery-mode)

doom-modeline

(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))

evil

(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)

evil-anzu

Show match counts in modeline

(use-package evil-anzu
  :after (evil)
  :config
  (global-anzu-mode))

evil-collection

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))

git-link

Quickly create links to files and commits

(use-package git-link
  :config
  (ads/leader-keys
    "gf" 'git-link
    "gF" 'git-link-dispatch))

helpful

(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)

insert-variable-value

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)

magit

(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))

marginalia

(use-package marginalia
  :ensure t
  :demand t
  :hook (after-init . marginalia-mode))

markdown

(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)

nerd-icons

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))

nix

(use-package nix-mode
  :config
  (global-nix-prettify-mode))

nov (epub)

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)))

orderless

(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-overrides '((file (styles basic partial-completion)))))

org

(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))

org keybindings

Set custom org mode bindings and functions.
(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)

org-agenda

(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)

org-appear

(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))))

org-babel

(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>>

config

Structured Templates

(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))

Languages

(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")
	       ))

org-capture

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)

capture-inbox

("i" "inbox" plain
(file ads/inbox-file)
"* %^{}
:CREATED: %U
"
:immediate-finish)

capture-task

("t" "todo" plain
(file ads/inbox-file)
"* TODO %?
SCHEDULED: %t

")

capture-note

("n" "note" plain
(file ads/inbox-file)
"* %^{HEADING}
:CREATED: %U

%?
")

org-modern

(use-package org-modern
  :after (org)
  :custom
  (org-modern-fold-stars
   '(("" . "")
     ("" . "")
     ("" . "")
     ("" . "")
     ("" . "")
     ("" . "")
     ("" . "")
     ("" . "")))
  (org-modern-checkbox
      '((88 . "")
	(45 . "")
	(32 . "")))
  :config
  (global-org-modern-mode)
  )

org-roam

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")

roam-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)

roam-categories

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)

roam-insert-immediate

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)

roam-node-display

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

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

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

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)

rainbow-delimiters

(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))

rainbow-mode

(use-package rainbow-mode
  :commands (rainbow-mode))

read-only-directories

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)

recentf

(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

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))

save-hist

(use-package savehist
  :config
  (savehist-mode 1))

sudo-edit

(use-package sudo-edit)

treemacs

(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"))

ultra-scroll

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))

vertico

(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

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))

Computer specific configs

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")))

Windows

Work forces me to use windows as much as I’d rather not.

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\\"))

Copy files to .emacs.d on tangle

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"))

AHK

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)
	      )
  )

Window Spy

(defun ahk-launch-window-spy ()
  (interactive)
  (w32-shell-execute 1 "C:/Users/adanaos/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/AutoHotkey Window Spy.lnk"))

Align windows theme with emacs

;; 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)

Browse in Edge

Some things only work in edge on my work computer

(defun win/browse-url-edge (url)
    (shell-command (concat "start msedge " url)))

Hide DOS EOL

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 []))

Exec .bat in new cmd window

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)))

Org-attatch dir in windows explorer

(defun org-attatch-open-win-explorer ()
  (interactive)
  (w32-shell-execute 1 (org-attach-dir-get-create)))

Dired open in windows default

(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

(provide 'ms-windows.el)

Linux

nixconfig

jump to my nix config files

(ads/leader-keys
  "cn" '((lambda () (interactive) (find-file "/etc/nixos/configuration.nix"))
	 :which-key "ni config"))

About

My Emacs Config

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published