Volume II — Advanced Reference

Shortcuts & Deep Customization

A comprehensive deep-dive into every keyboard shortcut category, the complete hooks and advice system, keybinding architecture, theme customization, and macOS-specific tuning.

27 Sections Keyboard Macros Hooks & Advice Daemon Mode Custom Themes Performance
PART I Complete Keyboard Shortcut Reference Sections 1–13

01 Modifier Key Patterns

Emacs keybindings follow a consistent logic. Learning the patterns lets you predict and remember bindings rather than memorizing each one individually.

The modifier hierarchy

PatternScopeExamples
C- (Control)Character-level operationsC-f char forward, C-d delete char
M- (Meta/Option)Word/sexp-level operationsM-f word forward, M-d delete word
C-M- (Control+Meta)Expression/structural levelC-M-f sexp forward, C-M-d down list
C-x prefixGlobal actions (files, buffers, frames)C-x C-f find-file, C-x b switch-buffer
C-c prefixMajor-mode commands (user/mode-specific)C-c C-c execute, C-c C-t org-todo
C-h prefixHelp systemC-h k describe-key, C-h f describe-function
s- (Super/⌘)macOS-native operationss-s save, s-c copy, s-f find

Parallel motion bindings

Notice how the same letter does the same conceptual thing at different scales depending on modifier:

KeyC- (character)M- (word)C-M- (sexp)
fForward characterForward wordForward sexp
bBackward characterBackward wordBackward sexp
dDelete characterKill word forwardDown into list
kKill to end of lineKill to end of sentenceKill sexp
aBeginning of lineBeginning of sentenceBeginning of defun
eEnd of lineEnd of sentenceEnd of defun
h(help prefix)Mark paragraphMark defun

Prefix argument (universal argument)

Key sequenceEffect
C-uMultiply next command by 4 (default prefix)
C-u C-uMultiply by 16
C-u 8Prefix argument of 8
M-5Prefix argument of 5 (shorthand)
C-u 0 C-x eRepeat keyboard macro until error
C-u C-SPCJump to previous mark position
C-u C-sRepeat last search regexp
💡 Prefix changes meaning, not just count

Many commands behave qualitatively differently with a prefix argument rather than just repeating. C-u C-l scrolls so the current line is at the top (not middle). C-u M-q justifies text (not just fills it). C-u C-x = gives detailed character info. Always try C-h k + the key to see what the prefix does.

02 Sexp & Structural Navigation

A sexp (s-expression) is a balanced unit of code: a word, number, string, or anything in matching delimiters () [] {} "". These bindings work in all programming modes.

KeyAction
Horizontal movement
C-M-fMove forward over one sexp
C-M-bMove backward over one sexp
C-M-nMove forward over list (parenthesized group)
C-M-pMove backward over list
Vertical movement (nesting)
C-M-dMove down into list (into parentheses)
C-M-uMove up out of list (out of parentheses)
Defun navigation
C-M-aMove to beginning of current function/defun
C-M-eMove to end of current function/defun
C-M-hMark the current function (select it)
Killing by structure
C-M-kKill sexp forward
C-M-tTranspose sexps
C-M-@Mark sexp (select balanced expression)
Indentation
C-M-qRe-indent the current sexp
C-M-\Indent the selected region

Paredit & Smartparens

Install paredit or smartparens for structured editing — these packages keep parentheses balanced as you type and add power-user operations:

;; Paredit — strict structural editing
(use-package paredit
  :hook ((emacs-lisp-mode lisp-mode clojure-mode) . enable-paredit-mode))

;; Smartparens — more flexible alternative
(use-package smartparens
  :hook (prog-mode . smartparens-mode)
  :config (require 'smartparens-config))
Paredit keyAction
C-)Slurp forward — pull next sexp into current list
C-}Barf forward — push last sexp out of current list
C-(Slurp backward
C-{Barf backward
M-sSplice — remove surrounding parentheses
M-rRaise — replace parent sexp with current sexp
M-SSplit list at point
M-JJoin two adjacent lists

03 Advanced Editing Operations

Sorting & Aligning

Command (M-x)Action
sort-linesAlphabetically sort lines in region
sort-wordsSort words in region alphabetically
sort-columnsSort lines by the column content in region
reverse-regionReverse the order of lines in region
align-regexpAlign text to a regexp pattern (great for tables)
alignAlign by current mode's alignment rules
delete-duplicate-linesRemove duplicate lines in region (Emacs 29+)

Whitespace manipulation

KeyAction
M-\Delete all whitespace around point
M-SPCCollapse whitespace to a single space
C-x C-oDelete blank lines around point
M-^Join current line with previous line
C-M-oSplit line at point (move rest to new line)
M-x delete-trailing-whitespaceRemove trailing spaces from all lines
M-x untabifyConvert tabs to spaces in region
M-x tabifyConvert spaces to tabs where possible

Zap-to-char and variations

KeyAction
M-z cZap to character c — kill from point to next occurrence of c
M-Z cZap up to (not including) character c (Emacs 29+)
C-u M-z cZap to Nth occurrence of c

Multiple cursors (package)

;; Install multiple-cursors
(use-package multiple-cursors
  :bind (("C-S-c C-S-c" . mc/edit-lines)
         ("C->"         . mc/mark-next-like-this)
         ("C-<"         . mc/mark-previous-like-this)
         ("C-c C-<"     . mc/mark-all-like-this)
         ("s-d"         . mc/mark-next-like-this)))
KeyAction
C->Add cursor at next occurrence of current word/region
C-<Add cursor at previous occurrence
C-c C-<Add cursors at all occurrences in buffer
C-S-c C-S-cEdit all lines in region simultaneously
C-gReturn to single cursor

04 Rectangle Operations

A rectangle is defined by the top-left and bottom-right corners of a column selection. Mark a region normally (with C-SPC) and then apply rectangle operations to the column block it defines.

KeyAction
C-x r kKill rectangle (cut)
C-x r M-wCopy rectangle to kill ring (Emacs 29+)
C-x r yYank (paste) last killed rectangle
C-x r dDelete rectangle (remove text, close gap)
C-x r cClear rectangle (replace with spaces)
C-x r oOpen rectangle (insert blank space columns)
C-x r tReplace rectangle with typed string (each line)
C-x r NInsert line numbers into rectangle
C-x r r rCopy rectangle to register r
C-x r i rInsert rectangle from register r

Rectangle Mark Mode (Emacs 26+)

For a more visual experience, use rectangle-mark-mode to see a highlighted column selection:

KeyAction
C-x SPCToggle rectangle-mark-mode — visually highlights column
💡 Rectangle use case: add column prefix

Position point at column 0, line 1. Set mark, move to end of the block. Then C-x r t and type // to comment out every line in the block simultaneously. Or use C-x r o to insert space before each line for indentation adjustments.

05 Registers & Bookmarks

Registers are single-character named slots that can hold positions, text, rectangles, numbers, window configurations, or framesets. They reset when Emacs exits.

Register overview

KeyStores…Retrieves with…
C-x r SPC rBuffer position (point)C-x r j r
C-x r s rSelected region as textC-x r i r
C-x r r rRectangleC-x r i r
C-x r w rWindow configurationC-x r j r
C-x r f rFrameset (all frames + windows)C-x r j r
C-x r n rNumber (integer)C-x r i r inserts it
C-x r + rIncrement number in register r

Viewing registers

CommandAction
M-x list-registersShow all registers and their contents
M-x view-registerDisplay a specific register's contents

Bookmarks — persistent across sessions

Unlike registers, bookmarks are saved to disk (~/.emacs.d/bookmarks) and persist between Emacs sessions.

KeyAction
C-x r mSet a bookmark at point (prompts for name)
C-x r bJump to a bookmark (with completion)
C-x r lList all bookmarks in *Bookmark List* buffer
M-x bookmark-saveManually save bookmarks to disk now
M-x bookmark-deleteDelete a bookmark by name
M-x bookmark-renameRename a bookmark

In the Bookmark List buffer

  • d — mark for deletion, x — execute deletions
  • r — rename bookmark, e — edit annotation
  • o — open in other window, RET — open in this window

06 Keyboard Macros

Keyboard macros let you record a sequence of keystrokes and replay them, either once or thousands of times. They're one of Emacs' most powerful productivity features.

Start recordingC-x (
Do your editingtype, move, etc.
Stop recordingC-x )
Execute onceC-x e
Repeat N timesC-u 50 C-x e
KeyAction
Basic recording & playback
C-x ( or F3Start recording a keyboard macro
C-x ) or F4Stop recording (or execute last macro if not recording)
C-x eExecute last keyboard macro once; press e repeatedly to repeat
C-u 0 C-x eExecute macro indefinitely until an error (end of buffer, etc.)
C-u N C-x eExecute macro exactly N times
Naming and saving
C-x C-k nName the last defined macro (makes it an Emacs command)
C-x C-k bBind last macro to a key (prompts for key)
M-x insert-kbd-macroInsert Lisp definition of named macro into buffer (for saving)
Editing macros
C-x C-k eOpen the macro editor (kbd-macro-edit)
C-x C-k rApply macro to each line in region
C-x C-k C-tSwap the last two macros in the ring
Macro ring
C-x C-k C-pPrevious macro in the macro ring
C-x C-k C-nNext macro in the macro ring

Inserting a query in a macro

During recording, press C-x q to insert a pause point. When the macro runs, Emacs will stop and prompt: press SPC to continue, DEL to skip this iteration, or RET to exit macro execution.

Saving macros between sessions

;; After naming a macro (C-x C-k n my-macro), save it in init.el:
M-x insert-kbd-macro RET my-macro RET
;; This inserts the defun into your current buffer — move it to init.el
⚡ Step-edit macros

Press C-x C-k C-e to open the step-edit interface. You can walk through a macro step by step — substituting different keystrokes mid-playback, inserting new steps, or deleting wrong ones. Essential for fixing complex macros.

07 Narrowing & Folding

Narrowing

Narrowing restricts the visible (and editable) portion of a buffer to a region. The rest of the buffer is invisible and untouchable — extremely useful for focused editing or batch operations on a subset of a file.

KeyAction
C-x n nNarrow to region (between point and mark)
C-x n wWiden — restore full buffer view
C-x n dNarrow to current defun (function)
C-x n pNarrow to current page (delimited by ^L)
C-x n bNarrow to org block (in org-mode)
C-x n sNarrow to org subtree (in org-mode)
💡 Enable narrowing

Narrowing is disabled by default (Emacs asks if you really meant it the first time). Enable it cleanly: (put 'narrow-to-region 'disabled nil) in your init.el.

Hide-Show (hs-minor-mode)

Built-in code folding for structured languages. Enable with M-x hs-minor-mode.

KeyAction
C-c @ C-cToggle hide/show of current block
C-c @ C-hHide current block
C-c @ C-sShow current block
C-c @ C-M-hHide all blocks in buffer
C-c @ C-M-sShow all blocks in buffer

08 Advanced Search Techniques

Isearch extras

Key (during isearch)Action
M-s .Search for symbol under cursor (respects word boundaries)
M-s wToggle word search mode (whole words only)
M-s _Toggle symbol search mode
M-s rToggle regexp mode during isearch
M-s cToggle case-sensitivity during search
M-eEdit the search string in minibuffer
M-pPrevious search string (isearch history)
M-nNext search string (isearch history)
C-wAdd word at point to search string
C-yYank rest of line into search string
M-TABComplete search string from available choices
C-M-wDelete last character added to search string

Occur — list all matches

KeyAction
M-s oRun occur with the current search term
M-x occurPrompt for a regexp, show all matching lines
M-x multi-occurRun occur across multiple buffers
M-x multi-occur-in-matching-buffersOccur in buffers matching a regexp name

In the *Occur* buffer:

  • n / p — next / previous match
  • RET — go to match in source buffer
  • o — go to match in other window
  • e — enter occur-edit-mode (edit matches in place)
  • C-c C-c — finish editing (in occur-edit-mode)
  • q — quit *Occur*

Highlight commands

KeyAction
M-s h rHighlight lines matching a regexp (different color each call)
M-s h lHighlight phrases matching a regexp
M-s h pHighlight phrase (literal string)
M-s h uUnhighlight regexp under cursor
M-s h wHighlight word at point
M-x unhighlight-regexpRemove a specific highlight

Grep integration

CommandAction
M-x grepRun grep and show results in a navigable buffer
M-x rgrepRecursive grep — prompts for pattern and directory
M-x lgrepLocal grep — searches the current directory
M-x grep-findRun find | grep

09 Abbrev, Dabbrev & Completion

Abbrev mode — text expansion

Define abbreviations that auto-expand as you type. For example, define btwby the way.

KeyAction
C-x a gAdd global abbrev (word before point → expansion you type)
C-x a lAdd local abbrev (only in current major mode)
C-x a i gInverse: type expansion first, then abbrev
C-x a eExplicitly expand abbrev at point
M-'Expand abbrev before point
M-x abbrev-modeToggle abbrev-mode (auto-expand on SPC/RET)
M-x list-abbrevsShow all defined abbreviations
M-x edit-abbrevsEdit abbrevs in a buffer
M-x write-abbrev-fileSave abbrevs to a file
;; Auto-load abbrevs and enable abbrev-mode globally
(setq save-abbrevs 'silently)
(setq-default abbrev-mode t)
(quietly-read-abbrev-file)

Dabbrev — dynamic completion from buffer

KeyAction
M-/Complete word by searching backward in buffer (dabbrev-expand)
M-/ againFind the next completion candidate
C-M-/Complete with list of all dabbrev candidates

Completion at point (capf)

KeyAction
C-M-i or M-TABComplete symbol at point (mode-aware)
TABIndent or complete (depends on context and mode)

Hippie Expand

A smarter expander that tries multiple completion strategies in order:

;; Replace dabbrev with hippie-expand (tries more strategies)
(global-set-key (kbd "M-/") 'hippie-expand)
(setq hippie-expand-try-functions-list
      '(try-expand-dabbrev
        try-expand-dabbrev-all-buffers
        try-expand-dabbrev-from-kill
        try-complete-file-name-partially
        try-complete-file-name
        try-expand-all-abbrevs
        try-expand-list
        try-expand-line
        try-complete-lisp-symbol-partially
        try-complete-lisp-symbol))

10 Shell & Process Integration

Inline shell commands

KeyAction
M-!Run shell command, show output in *Shell Command Output*
C-u M-!Run shell command and insert output at point
M-|Pipe selected region through shell command
C-u M-|Pipe region through command, replace region with output
M-&Run shell command asynchronously (doesn't block Emacs)

Shell buffers

CommandDescription
M-x shellStart an interactive shell buffer (uses $SHELL)
M-x eshellEmacs' own Lisp-based shell (no terminal needed)
M-x termFull terminal emulator buffer
M-x ansi-termTerminal with ANSI color support
M-x vtermlibvterm-based terminal (install separately; best performance)
M-x compileRun a compile command, navigate errors with C-x `
M-x async-shell-commandRun async command, show output in buffer

In shell buffers:

  • M-p / M-n — previous / next command in history
  • C-c C-p / C-c C-n — move to previous / next prompt
  • C-c C-c — interrupt current process (SIGINT)
  • C-c C-z — suspend process
  • C-c C-d — send EOF

Compilation mode navigation

KeyAction
C-x `Jump to next error in compilation output
M-g nNext error (works in compilation, grep, etc.)
M-g pPrevious error
🍎 macOS PATH in shell buffers

Shell buffers inside Emacs.app inherit Emacs' PATH, not your shell's. Install exec-path-from-shell and run (exec-path-from-shell-initialize) in your init.el to fix this.

11 Built-in Version Control (VC)

Emacs has a built-in VC layer that works with Git, SVN, Mercurial, and others. It's separate from Magit (which is more powerful) but useful when Magit isn't installed.

KeyAction
Core operations
C-x v vvc-next-action — stage + commit current file (smart)
C-x v +Update from remote (pull)
C-x v iRegister file with version control
Viewing changes
C-x v =Show diff between working file and last revision
C-x v DShow diff for entire directory
C-x v lShow commit log for current file
C-x v LShow commit log for current directory
C-x v aShow annotated file (blame)
Branching & misc
C-x v bSwitch branch
C-x v sCreate a snapshot (tag)
C-x v uRevert file to last revision
C-x v gAnnotate with git-blame

12 Mode-Specific Key Bindings

Emacs Lisp mode

KeyAction
C-M-xEvaluate the defun at or before point
C-x C-eEvaluate sexp before point
C-c C-bEvaluate entire buffer
M-TABComplete Lisp symbol at point

Python mode (python-ts-mode / python-mode)

KeyAction
C-c C-cSend buffer to Python process
C-c C-rSend region to Python process
C-c C-zSwitch to Python REPL buffer
C-c C-dPydoc for symbol at point

Markdown mode (package)

KeyAction
C-c C-s bInsert bold
C-c C-s iInsert italic
C-c C-s cInsert code span
C-c C-s CInsert code block
C-c C-c pPreview in browser

Dired (revisited — advanced keys)

KeyAction
M-%Query-replace in all marked files
QQuery replace across all marked files
!Run shell command on marked files
ASearch across all marked files (regexp)
WOpen marked files in external browser
mMark file, u — unmark, U — unmark all
* .Mark all files with a given extension
* /Mark all directories
iInsert subdirectory into Dired listing
wdired (C-x C-q)Edit filenames inline (writable Dired)

13 Winner Mode, Tab Bar & Window History

Winner mode — undo window changes

(winner-mode 1)  ; enable in init.el
KeyAction
C-c ←Undo last window configuration change
C-c →Redo window configuration change

Tab Bar (Emacs 27+)

Emacs has a native tab bar for window configurations — each tab holds an independent set of windows.

(tab-bar-mode 1)  ; enable native tab bar
KeyAction
C-x t 2Create new tab
C-x t 0Close current tab
C-x t oSwitch to next tab
C-x t rRename current tab
C-x t bSwitch to buffer in a specific tab
C-x t fOpen file in new tab
s-} / s-{Next / previous tab (macOS-style, if configured)

Follow mode

Split a buffer into two side-by-side windows and follow-mode keeps them synchronized, showing consecutive pages of the same buffer:

(M-x) follow-mode   ; toggle in any buffer after C-x 3
PART II Customization in Depth Sections 14–27

14 Configuration Architecture

A well-organized Emacs configuration avoids a monolithic init.el that grows unmanageable. Here are the main file locations and architectural patterns.

File locations

FilePurposeLoaded when
~/.config/emacs/early-init.elPre-GUI, pre-package setupBefore anything else (Emacs 27+)
~/.config/emacs/init.elMain config entry pointAfter early-init, after packages
~/.config/emacs/lisp/Your custom Elisp modulesWhen you require them
~/.config/emacs/custom.elEmacs' auto-generated customize outputWhen you call (load custom-file)

Modular config pattern

;; init.el — loads other modules
(add-to-list 'load-path
             (expand-file-name "lisp" user-emacs-directory))

;; Separate concerns into their own files
(require 'init-ui)        ; UI settings, theme, fonts
(require 'init-editing)   ; editing prefs, whitespace
(require 'init-completion) ; vertico, orderless, etc
(require 'init-git)       ; magit config
(require 'init-org)       ; org-mode setup
(require 'init-langs)     ; language-specific config
(require 'init-macos)     ; macOS-specific tweaks

;; Keep Customize's output out of init.el
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file) (load custom-file))
💡 Keep custom.el separate

Without specifying custom-file, Emacs appends auto-generated custom-set-variables blocks to the end of your init.el after running M-x customize. This pollutes hand-written config. Point it to a separate file and load it explicitly.

15 early-init.el

early-init.el is loaded before the package system and GUI are initialized (Emacs 27+). Use it for startup performance and pre-frame configuration.

;; ~/.config/emacs/early-init.el

;; ── PERFORMANCE: raise GC threshold during startup ──
(setq gc-cons-threshold most-positive-fixnum)
(setq gc-cons-percentage 0.6)

;; Restore reasonable GC after startup completes
(add-hook 'emacs-startup-hook
  (lambda ()
    (setq gc-cons-threshold 16777216)  ; 16MB
    (setq gc-cons-percentage 0.1)))

;; ── DISABLE PACKAGE.EL early if using straight.el ──
;; (setq package-enable-at-startup nil)

;; ── PREVENT FLASH OF UNSTYLED UI ──────────────────
;; Suppress UI elements before they paint
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

;; Set frame size before display (avoids resize flash)
(push '(width . 180) default-frame-alist)
(push '(height . 50)  default-frame-alist)

;; ── macOS SPECIFIC ──────────────────────────────────
;; Transparent title bar (native-comp builds)
(push '(ns-transparent-titlebar . t) default-frame-alist)
(push '(ns-appearance . dark)        default-frame-alist)

;; ── NATIVE COMPILATION (Emacs 28+) ─────────────────
(when (featurep 'native-compile)
  ;; Silence compiler warnings in *Warnings* buffer
  (setq native-comp-async-report-warnings-errors 'silent)
  (setq native-comp-deferred-compilation t))
⚡ Native compilation

Emacs 28+ can compile Elisp to native machine code via libgccjit, giving 40–80× speed gains for compute-intensive code. Install with brew install emacs-plus@29 --with-native-comp. Packages are compiled asynchronously on first load, and cached at ~/.cache/emacs/eln-cache/. After initial setup the speed gains are permanent.

16 use-package Deep Dive

use-package (built into Emacs 29+) is the standard way to declare, configure, and lazy-load packages. Every keyword controls a different aspect of the package lifecycle.

;; Fully annotated use-package example
(use-package some-package
  ;; :ensure — auto-install from package archive
  :ensure t

  ;; :pin — install only from a specific archive
  :pin melpa-stable

  ;; :demand — force eager loading (no lazy load)
  :demand t

  ;; :defer — delay loading (t = any positive trigger)
  ;; :defer 2 = load 2 seconds after startup
  :defer 2

  ;; :after — load only after these packages are loaded
  :after (other-pkg another-pkg)

  ;; :if — only use-package if condition is true
  :if (eq system-type 'darwin)

  ;; :when / :unless — conditional loading aliases
  :when window-system

  ;; :init — runs BEFORE package is loaded
  :init
  (setq some-var 42)

  ;; :config — runs AFTER package is loaded
  :config
  (some-package-mode 1)
  (setq some-package-setting t)

  ;; :custom — sets variables via customize system
  :custom
  (some-package-delay 0.3)
  (some-package-feature-enabled t "Enable the feature")

  ;; :custom-face — sets faces via customize
  :custom-face
  (some-package-face ((t (:foreground "#2dd4bf"))))

  ;; :bind — global key bindings (also causes deferred load)
  :bind
  ("C-c s" . some-command)
  ("C-c S" . some-other-command)

  ;; :bind-keymap — bind a prefix key to a keymap
  :bind-keymap
  ("C-c p" . some-package-map)

  ;; :bind* — override mode-local bindings globally
  :bind*
  ("C-j" . some-very-important-command)

  ;; :hook — add to hooks (loads package when hook fires)
  :hook
  (prog-mode . some-package-mode)
  ((text-mode org-mode) . some-package-mode)

  ;; :mode — auto-activate for file extensions
  :mode
  ("\\.xyz\\'" . some-package-mode)

  ;; :interpreter — auto-activate for script shebangs
  :interpreter ("python3" . some-package-mode)

  ;; :magic — activate based on file content (magic bytes)
  :magic ("\\(BEGIN\\)" . some-package-mode)

  ;; :commands — declare commands so autoloads work
  :commands (some-command some-other-command)

  ;; :functions / :defines — suppress byte-compiler warnings
  :functions (some-external-fn)
  :defines  (some-external-var)

  ;; :no-require — don't (require 'some-package) in :config
  :no-require t

  ;; :preface — evaluated first, before everything else
  :preface
  (defun my-helper () ...))

Practical patterns

;; Vertico + Orderless + Marginalia (completion stack)
(use-package vertico
  :init (vertico-mode)
  :custom (vertico-cycle t))

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

(use-package marginalia
  :after vertico
  :init (marginalia-mode))

(use-package consult
  :bind (("C-x b"   . consult-buffer)
         ("M-y"     . consult-yank-pop)
         ("M-g g"   . consult-goto-line)
         ("C-c r"   . consult-recent-file)
         ("C-s"     . consult-line)))

17 The Hooks System

Hooks are lists of functions called at specific points in Emacs' lifecycle. They're the primary mechanism for mode-dependent and event-driven configuration.

Event fires prog-mode-hook [display-line-numbers-mode, flycheck-mode, company-mode, ...]
;; Basic hook usage
(add-hook 'prog-mode-hook  #'display-line-numbers-mode)
(add-hook 'before-save-hook #'delete-trailing-whitespace)
(add-hook 'text-mode-hook   #'visual-line-mode)

;; Remove a hook
(remove-hook 'prog-mode-hook #'display-line-numbers-mode)

;; Hook with an inline lambda
(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (setq-local tab-width 2)
            (flycheck-mode -1)))

;; Depth argument: lower runs earlier (-100 to 100, default 0)
(add-hook 'after-init-hook #'my-startup-fn 90)  ; run late
(add-hook 'after-init-hook #'early-setup-fn -50) ; run early

Essential hooks reference

Hook nameWhen it fires
Startup & init
emacs-startup-hookAfter init.el is fully loaded (best for most startup tasks)
after-init-hookAfter normal init, before emacs-startup-hook
window-setup-hookAfter frame is fully set up and visible
File & buffer
find-file-hookAfter visiting any file
before-save-hookJust before saving a buffer
after-save-hookJust after saving a buffer
kill-buffer-hookBefore killing a buffer
after-revert-hookAfter reverting a buffer from disk
Mode hooks (each major mode has one)
prog-mode-hookAny programming mode (parent of python-mode, etc.)
text-mode-hookAny text mode (parent of org-mode, markdown-mode, etc.)
emacs-lisp-mode-hookSpecifically Emacs Lisp files
org-mode-hookOrg-mode files
Minibuffer
minibuffer-setup-hookWhen minibuffer opens
minibuffer-exit-hookWhen minibuffer closes

Mode-local variables with setq-local

;; Set variable only in this buffer (not globally)
(add-hook 'python-mode-hook
          (lambda ()
            (setq-local fill-column 88)         ; Black formatter width
            (setq-local tab-width 4)
            (setq-local indent-tabs-mode nil)))

18 The Advice System

Advice lets you modify the behavior of any existing Emacs function without changing its source code. You wrap it with code that runs before, after, or around the original.

:before your code runs first — can inspect args
:original the-advised-function (runs normally)
:after your code runs last — can inspect result
;; Modern advice API (advice-add)

;; :before — run before the original, can see args
(advice-add 'find-file :before
  (lambda (filename &optional wildcards)
    (message "Opening: %s" filename)))

;; :after — run after the original
(advice-add 'save-buffer :after
  (lambda (&rest args)
    (message "Saved at %s" (format-time-string "%H:%M:%S"))))

;; :around — wraps the original; you control if/when it runs
;; orig-fn is the original function passed as first arg
(advice-add 'message :around
  (lambda (orig-fn &rest args)
    (unless (string-match-p "^Loading" (car args))
      (apply orig-fn args))))  ; suppress "Loading..." messages

;; :override — completely replaces the original
(advice-add 'yes-or-no-p :override #'y-or-n-p)  ; always use y/n

;; :before-while — run before; if returns nil, skip original
;; :before-until — run before; if returns non-nil, skip original
;; :after-while  — run after; original must return non-nil
;; :filter-args  — transform args before passing to original
;; :filter-return — transform original's return value

;; Remove advice
(advice-remove 'find-file 'my-find-file-advice)

;; Name an advice so you can remove it later
(advice-add 'kill-buffer :before #'my-before-kill :name "my-kill-advice")
(advice-remove 'kill-buffer "my-kill-advice")

Practical advice examples

;; Make C-x C-c ask for confirmation before quitting
(advice-add 'save-buffers-kill-terminal :before-while
  (lambda (&rest _)
    (y-or-n-p "Really quit Emacs? ")))

;; Automatically create parent directories when visiting a new file
(advice-add 'find-file :before
  (lambda (filename &rest _)
    (let ((dir (file-name-directory filename)))
      (when (and dir (not (file-exists-p dir)))
        (make-directory dir t)))))

19 Keybindings in Depth

The keymap hierarchy

Emacs looks up a keybinding through a hierarchy of keymaps. The most local wins:

  1. overriding-local-map — highest priority (rarely used)
  2. Text properties — key maps on text (links, buttons)
  3. Minor mode maps — active minor modes, in reverse order of activation
  4. Local map — the current major mode's keymap
  5. Global map — the fallback for everything

Binding functions

;; global-set-key — adds to the global map
(global-set-key (kbd "C-c j") 'jump-to-register)

;; define-key — more flexible, specify the keymap
(define-key global-map (kbd "C-c j") 'jump-to-register)
(define-key org-mode-map (kbd "C-c ]") 'org-ref-insert-link)
(define-key emacs-lisp-mode-map (kbd "C-c C-e") 'eval-defun)

;; local-set-key — set in current buffer's local map
(local-set-key (kbd "C-c x") 'my-local-command)

;; keymap-global-set (Emacs 29+ cleaner API)
(keymap-global-set "C-c j" 'jump-to-register)
(keymap-set org-mode-map "C-c ]" 'org-ref-insert-link)

Creating prefix maps

;; Create your own C-c p prefix for personal commands
(define-prefix-command 'my-personal-map)
(global-set-key (kbd "C-c p") 'my-personal-map)

(define-key my-personal-map (kbd "d") 'my-insert-date)
(define-key my-personal-map (kbd "f") 'my-find-file)
(define-key my-personal-map (kbd "r") 'my-reload-config)

Unbinding keys

;; Unbind a key (set to nil)
(global-unset-key (kbd "C-z"))  ; disable suspend-frame
(global-unset-key (kbd "s-p"))  ; disable macOS print
(global-unset-key (kbd "s-t"))  ; disable macOS new tab

;; Or bind to ignore (passes no-op)
(global-set-key (kbd "C-z") 'ignore)

Hydra — transient key menus

;; Hydra: hold a key, get a temporary keymap with hints
(use-package hydra)

(defhydra hydra-zoom (global-map "C-=")
  "zoom"
  ("+" text-scale-increase "in")
  ("-" text-scale-decrease "out")
  ("0" (text-scale-set 0) "reset" :exit t)
  ("q" nil "quit" :exit t))

;; After C-=, press +/-/0/q to control zoom — hint shown in echo area

Which-key configuration

;; which-key shows available next keys as you type a prefix
(use-package which-key
  :init (which-key-mode)
  :custom
  (which-key-idle-delay 0.5)        ; show after 0.5s pause
  (which-key-max-display-columns 4) ; 4 columns
  (which-key-sort-order 'which-key-key-order-alpha)
  :config
  ;; Add descriptions to your prefix keys
  (which-key-add-key-based-replacements
    "C-c p"   "personal"
    "C-x r"   "registers"
    "C-x 4"   "other-window"
    "C-c &"   "yasnippet"))

20 Faces, Themes & Colors

A face is a named set of text display properties (foreground color, background, font weight, underline, etc.). Every visual element in Emacs — syntax highlighting, the mode line, the cursor — is controlled by a face.

Setting faces in init.el

;; set-face-attribute — the primary face configuration function
;; Arguments: face, frame (nil = all frames), then keyword/value pairs
(set-face-attribute 'default nil
  :family  "JetBrains Mono"
  :height  140            ; height in 1/10ths of a point
  :weight  'regular)

;; Set a specific coding font just for comments
(set-face-attribute 'font-lock-comment-face nil
  :slant  'italic
  :family "Menlo")

;; Customize the cursor
(set-default 'cursor-type 'bar)    ; bar | box | hollow | hbar
(set-face-attribute 'cursor nil :background "#2dd4bf")

;; Customise fringe
(set-face-attribute 'fringe nil
  :background "#f2f0eb")  ; match page background

;; Make line numbers less intrusive
(set-face-attribute 'line-number nil
  :foreground "#c0bcc0"
  :background "#f8f7f2")

Defining your own face

(defface my-important-face
  '((((class color) (min-colors 88) (background dark))
     (:foreground "#fbbf24" :weight bold :underline t))
    (((class color) (min-colors 88) (background light))
     (:foreground "#92400e" :weight bold :underline t))
    (t (:weight bold)))
  "Face for important text."
  :group 'my-faces)

Loading and customizing themes

;; Load a built-in theme
(load-theme 'modus-operandi t)   ; light — best for Retina
(load-theme 'modus-vivendi t)    ; dark

;; Or use doom-themes
(use-package doom-themes
  :config
  (load-theme 'doom-one t)
  (doom-themes-visual-bell-config)
  (doom-themes-org-config))

;; Override specific faces AFTER loading a theme
(with-eval-after-load 'doom-themes
  (set-face-attribute 'line-number-current-line nil
    :foreground "#2dd4bf"
    :weight 'bold)
  (set-face-attribute 'mode-line nil
    :height 120))    ; 12pt mode line text

Writing a minimal theme from scratch

;; Save as ~/.config/emacs/themes/my-theme-theme.el
(deftheme my-theme "My personal theme.")

(custom-theme-set-faces
 'my-theme
 `(default         ((t (:background "#1a1b2e" :foreground "#e2d9f3"))))
 `(cursor          ((t (:background "#2dd4bf"))))
 `(fringe          ((t (:background "#1a1b2e"))))
 `(mode-line       ((t (:background "#0d1117" :foreground "#8b949e"
                        :box (:line-width 1 :color "#21262d")))))
 `(mode-line-inactive ((t (:background "#161b22" :foreground "#484f58"))))
 `(font-lock-keyword-face     ((t (:foreground "#ff7b72" :weight bold))))
 `(font-lock-string-face      ((t (:foreground "#a5d6ff"))))
 `(font-lock-comment-face     ((t (:foreground "#6e7681" :slant italic))))
 `(font-lock-function-name-face ((t (:foreground "#79c0ff" :weight bold))))
 `(font-lock-type-face        ((t (:foreground "#ffa657"))))
 `(font-lock-variable-name-face ((t (:foreground "#e6edf3"))))
 `(org-level-1  ((t (:foreground "#2dd4bf" :weight bold :height 1.3))))
 `(org-level-2  ((t (:foreground "#a78bfa" :weight bold :height 1.15))))
 `(region       ((t (:background "#2d313a")))))

(custom-theme-set-variables
 'my-theme
 '(line-spacing 0.15))

(provide-theme 'my-theme)

;; In init.el, load it:
(add-to-list 'custom-theme-load-path
             (expand-file-name "themes" user-emacs-directory))
(load-theme 'my-theme t)

21 Mode Line Customization

The mode line is fully customizable through the mode-line-format variable — a list of elements that are evaluated and concatenated.

Built-in elements

;; Default mode-line-format for reference
mode-line-format
;; ⇒ ("%e" mode-line-front-space mode-line-mule-info
;;     mode-line-client mode-line-modified mode-line-remote
;;     mode-line-frame-identification mode-line-buffer-identification
;;     "   " mode-line-position
;;     (vc-mode vc-mode) "  " mode-line-modes mode-line-misc-info)
;; Custom minimal mode line
(setq-default mode-line-format
  '("%e"
    ; modified indicator: ** if modified, -- if not
    (:eval (if (buffer-modified-p) " ◉ " " ○ "))
    ; buffer name
    mode-line-buffer-identification
    "  "
    ; line:column position
    ("%l:%c")
    "  "
    ; major mode
    mode-name
    "  "
    ; git branch (if vc is active)
    (:eval (when vc-mode
             (concat " ⎇ "
                     (string-trim vc-mode "^ Git[:-]")))))))

doom-modeline configuration

(use-package doom-modeline
  :init (doom-modeline-mode 1)
  :custom
  (doom-modeline-height 28)
  (doom-modeline-bar-width 4)
  (doom-modeline-icon t)              ; requires nerd-icons
  (doom-modeline-major-mode-icon t)
  (doom-modeline-buffer-file-name-style 'truncate-upto-project)
  (doom-modeline-github nil)          ; disable GitHub notifications
  (doom-modeline-lsp t)
  (doom-modeline-time t))

;; Install icon fonts (run once)
(use-package nerd-icons
  :if (display-graphic-p))
;; M-x nerd-icons-install-fonts  ← run once to download fonts

22 Display & Visual Tweaks

Line display settings

;; Line numbers
(global-display-line-numbers-mode t)
(setq display-line-numbers-type 'relative)  ; or t, 'visual
(setq display-line-numbers-width 4)

;; Line wrapping
(setq-default truncate-lines t)    ; no wrapping by default
(global-visual-line-mode 1)        ; soft wrap at window edge

;; 80-column indicator
(global-display-fill-column-indicator-mode t)
(setq fill-column 80)
(set-face-attribute 'fill-column-indicator nil
  :foreground "#e0ddd8")

;; Line spacing (extra pixels between lines)
(setq-default line-spacing 0.2)   ; 20% of line height

Fringe and margin

;; Fringe width (left and right gutters)
(fringe-mode '(8 . 8))             ; 8px left, 8px right
(fringe-mode 0)                   ; no fringe at all

;; Left margin (extra text indent)
(setq-default left-margin-width  2)
(setq-default right-margin-width 2)

;; Padding around the text in a frame
(add-to-list 'default-frame-alist '(internal-border-width . 20))

Scrolling behavior

;; Smooth scrolling
(setq scroll-margin 5)             ; keep 5 lines of context
(setq scroll-conservatively 101)  ; scroll minimally
(setq scroll-preserve-screen-position t)

;; Pixel scrolling (Emacs 29+) — smooth like macOS
(when (fboundp 'pixel-scroll-precision-mode)
  (pixel-scroll-precision-mode t))

;; Mouse scrolling speed
(setq mouse-wheel-scroll-amount '(2 ((shift) . 5)))
(setq mouse-wheel-progressive-speed nil)  ; don't accelerate

Font configuration for macOS

;; Multiple fonts for different face categories
(defun my/setup-fonts ()
  "Set up fonts for macOS Retina display."
  ;; Monospace: JetBrains Mono or SF Mono
  (set-face-attribute 'default nil
    :family "JetBrains Mono" :height 140)
  ;; Variable pitch for prose (org, markdown)
  (set-face-attribute 'variable-pitch nil
    :family "SF Pro Text" :height 150)
  ;; Fixed pitch for code blocks in org
  (set-face-attribute 'fixed-pitch nil
    :family "JetBrains Mono" :height 130)
  ;; Emoji fallback
  (set-fontset-font t 'emoji (font-spec :family "Apple Color Emoji")))

(add-hook 'emacs-startup-hook #'my/setup-fonts)
(; Also run when creating a new frame)
(add-hook 'server-after-make-frame-hook #'my/setup-fonts)

;; Enable mixed-pitch in org-mode (variable pitch prose, fixed pitch code)
(use-package mixed-pitch
  :hook (org-mode . mixed-pitch-mode))

23 Directory-Local Variables

A .dir-locals.el file in any directory sets Emacs variables for all files in that directory tree. Essential for per-project settings without polluting your global config.

;; .dir-locals.el — place in your project root
((nil                               ; nil = applies to all modes
  (indent-tabs-mode . nil)
  (fill-column . 120)
  (require-final-newline . t))

 (python-mode                       ; only for Python files
  (python-indent-offset . 4)
  (flycheck-python-flake8-executable . "~/.venv/bin/flake8"))

 (c-mode                            ; only for C files
  (c-file-style . "linux")
  (tab-width . 8)
  (indent-tabs-mode . t))

 (js-mode
  (js-indent-level . 2)))

Create a .dir-locals.el file with M-x add-dir-local-variable (prompts for mode, variable, and value), or add one manually. Emacs asks for confirmation before loading unsafe variables.

Marking variables as safe

;; Silence the "risky variable" prompt for specific variables
(put 'python-indent-offset 'safe-local-variable #'integerp)
(put 'fill-column          'safe-local-variable #'integerp)
(put 'c-file-style         'safe-local-variable #'stringp)

24 Emacs Daemon Mode on macOS

Running Emacs as a daemon means it starts once and stays resident — opening files via emacsclient is near-instant. On macOS you can auto-start the daemon at login via launchd.

Manual daemon usage

# Start the daemon from terminal
$ emacs --daemon

# Open a file in the running daemon (GUI window)
$ emacsclient -c myfile.txt

# Open in terminal (no GUI frame)
$ emacsclient -t myfile.txt

# Open file in existing frame
$ emacsclient -n myfile.txt

# Shutdown the daemon gracefully
$ emacsclient -e '(kill-emacs)'

Shell aliases in ~/.zshrc

# Add to ~/.zshrc for convenience
alias e='emacsclient -c'           # open in new GUI frame
alias et='emacsclient -t'          # open in terminal
alias em='emacsclient -n'          # open, no wait
alias ekill='emacsclient -e "(kill-emacs)"'

# Set EDITOR/VISUAL to emacsclient for git, etc.
export EDITOR='emacsclient -t'
export VISUAL='emacsclient -c'

Auto-start at login with launchd

# Create plist file for launchd
$ mkdir -p ~/Library/LaunchAgents
<!-- ~/Library/LaunchAgents/gnu.emacs.daemon.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>gnu.emacs.daemon</string>

  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/emacs</string>   <!-- Apple Silicon -->
    <string>--fg-daemon</string>
  </array>

  <key>RunAtLoad</key>
  <true/>

  <key>KeepAlive</key>
  <true/>

  <key>StandardErrorPath</key>
  <string>/tmp/emacs-daemon.log</string>
</dict>
</plist>
# Load the plist into launchd (enable at login)
$ launchctl load -w ~/Library/LaunchAgents/gnu.emacs.daemon.plist

# Check status
$ launchctl list | grep emacs

# Unload (disable)
$ launchctl unload ~/Library/LaunchAgents/gnu.emacs.daemon.plist

init.el tweaks for daemon mode

;; Defer font/theme setup to when a frame is created
;; (daemon starts without a frame)
(add-hook 'server-after-make-frame-hook
  (lambda ()
    (when (display-graphic-p)
      (my/setup-fonts)
      (load-theme 'doom-one t))))

;; Prevent daemon from quitting when last client disconnects
(setq server-kill-new-buffers nil)
(add-hook 'server-done-hook
  (lambda ()
    (kill-buffer (current-buffer))))
🍎 macOS app alternative

If you use Emacs.app (from emacsformacosx.com), you can set it to open quickly from the Dock. The app itself handles its own startup; each double-click or Open-With re-uses the running instance. Combine with emacsclient by adding /Applications/Emacs.app/Contents/MacOS/bin to your PATH.

25 Performance Tuning

Measuring startup time

# Quick measurement from the terminal
$ emacs --batch --eval '(message "%.2f" (float-time (emacs-init-time)))'

# In-depth profiling with esup package
(use-package esup
  :commands esup)
;; Run: M-x esup

Key performance settings

;; ── GARBAGE COLLECTION ───────────────────────────
;; Use gcmh (Garbage Collector Magic Hack)
(use-package gcmh
  :init (gcmh-mode 1)
  :custom
  (gcmh-idle-delay 5)               ; GC after 5s idle
  (gcmh-high-cons-threshold 100000000)) ; 100MB threshold during work

;; ── FILE NAME HANDLER ────────────────────────────
;; Temporarily disable during startup (saves ~200ms)
(defvar my/file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
(add-hook 'emacs-startup-hook
  (lambda ()
    (setq file-name-handler-alist my/file-name-handler-alist)))

;; ── LSP PERFORMANCE ──────────────────────────────
(setq read-process-output-max (* 1024 1024)) ; 1MB
(setq lsp-idle-delay 0.5)
(setq lsp-log-io nil)             ; don't log LSP traffic

;; ── LAZY LOADING ─────────────────────────────────
;; Use :defer t in use-package where possible
(use-package magit :defer t)
(use-package org   :defer t)

;; ── FONT RENDERING ON MACOS ──────────────────────
(setq inhibit-compacting-font-caches t)  ; faster large-font rendering

Profiling with the built-in profiler

;; Start profiler, do slow action, then report
(M-x) profiler-start
;; ... do whatever is slow ...
(M-x) profiler-report   ; shows a tree of time spent per function
(M-x) profiler-stop

26 Writing Your Own Commands

Any function declared with (interactive) becomes a command that can be run with M-x or bound to a key. Here are useful patterns for macOS users.

;; ── BASIC INTERACTIVE COMMAND ────────────────────
(defun my/insert-date ()
  "Insert the current date in ISO format at point."
  (interactive)
  (insert (format-time-string "%Y-%m-%d")))
(global-set-key (kbd "C-c i d") #'my/insert-date)

;; ── COMMAND WITH PREFIX ARG ──────────────────────
(defun my/duplicate-line (n)
  "Duplicate the current line N times."
  (interactive "p")   ; "p" = numeric prefix arg
  (let ((line (buffer-substring (line-beginning-position)
                                  (line-end-position))))
    (dotimes (_ n)
      (end-of-line)
      (newline)
      (insert line))))
(global-set-key (kbd "C-c d") #'my/duplicate-line)

;; ── COMMAND PROMPTING FOR INPUT ──────────────────
(defun my/wrap-region (open close)
  "Wrap the selected region with OPEN and CLOSE strings."
  (interactive "sOpen with: \nsClose with: ")
  (let ((beg (region-beginning))
        (end (region-end)))
    (save-excursion
      (goto-char end)   (insert close)
      (goto-char beg)   (insert open))))

;; ── macOS: OPEN TERMINAL HERE ────────────────────
(defun my/open-terminal-here ()
  "Open macOS Terminal at current file's directory."
  (interactive)
  (let ((dir (if (buffer-file-name)
                  (file-name-directory (buffer-file-name))
                default-directory)))
    (shell-command
     (format "open -a Terminal %s"
             (shell-quote-argument dir)))))
(global-set-key (kbd "C-c o t") #'my/open-terminal-here)

;; ── TOGGLE BETWEEN LIGHT AND DARK THEME ─────────
(defvar my/dark-theme  'doom-one)
(defvar my/light-theme 'modus-operandi)
(defvar my/current-theme 'dark)

(defun my/toggle-theme ()
  "Toggle between light and dark theme."
  (interactive)
  (if (eq my/current-theme 'dark)
    (progn
      (disable-theme my/dark-theme)
      (load-theme my/light-theme t)
      (setq my/current-theme 'light))
    (progn
      (disable-theme my/light-theme)
      (load-theme my/dark-theme t)
      (setq my/current-theme 'dark))))
(global-set-key (kbd "C-c t t") #'my/toggle-theme)

;; ── RENAME CURRENT FILE ──────────────────────────
(defun my/rename-file-and-buffer (new-name)
  "Rename both current buffer and its visited file to NEW-NAME."
  (interactive (list (read-string "New name: "
                                    (buffer-name))))
  (let ((old-name (buffer-file-name)))
    (rename-file old-name new-name t)
    (rename-buffer new-name)
    (set-visited-file-name new-name)
    (set-buffer-modified-p nil)
    (message "File renamed to %s" new-name)))

27 Complete Production init.el for macOS

A fully-commented, macOS-optimized init.el integrating everything from this guide. Copy and adapt it as your starting point.

;; ─────────────────────────────────────────────────────────────────
;; ~/.config/emacs/init.el
;; Complete macOS Emacs configuration
;; ─────────────────────────────────────────────────────────────────

;; ══ PACKAGE SYSTEM ════════════════════════════════════════════════
(require 'package)
(setq package-archives
      '(("gnu"   . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(package-initialize)
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t
      use-package-always-defer  t)  ; lazy load everything by default

;; ══ KEEP CUSTOM OUT OF THIS FILE ═════════════════════════════════
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file) (load custom-file 'noerror))

;; ══ macOS MODIFIER KEYS ══════════════════════════════════════════
(setq mac-command-modifier      'super)    ; ⌘ = super
(setq mac-option-modifier       'meta)     ; ⌥ = meta
(setq mac-right-option-modifier 'none)     ; right ⌥ = normal chars
(setq mac-control-modifier      'control)  ; ⌃ = control
(setq ns-use-native-fullscreen  t)

;; macOS-style super keybindings
(global-set-key (kbd "s-s") #'save-buffer)
(global-set-key (kbd "s-c") #'kill-ring-save)
(global-set-key (kbd "s-v") #'yank)
(global-set-key (kbd "s-x") #'kill-region)
(global-set-key (kbd "s-z") #'undo)
(global-set-key (kbd "s-a") #'mark-whole-buffer)
(global-set-key (kbd "s-f") #'isearch-forward)
(global-set-key (kbd "s-g") #'isearch-repeat-forward)
(global-set-key (kbd "s-n") #'make-frame-command)
(global-set-key (kbd "s-w") #'delete-frame)
(global-set-key (kbd "s-`") #'other-frame)
(global-set-key (kbd "s-=") #'text-scale-increase)
(global-set-key (kbd "s--") #'text-scale-decrease)
(global-set-key (kbd "s-0") (lambda () (interactive) (text-scale-set 0)))
(global-set-key (kbd "s-<return>") #'toggle-frame-fullscreen)

;; ══ UI ════════════════════════════════════════════════════════════
(setq inhibit-startup-message  t
      inhibit-startup-echo-area-message (user-login-name))
(tool-bar-mode    -1)
(scroll-bar-mode  -1)
(blink-cursor-mode 0)
(column-number-mode 1)
(global-hl-line-mode 1)
(winner-mode 1)
(tab-bar-mode  0)       ; set to 1 if you want tabs
(fringe-mode '(8 . 4))

(setq-default
  cursor-type              'bar
  line-spacing             0.15
  fill-column              80
  truncate-lines           t)

(when (fboundp 'pixel-scroll-precision-mode)
  (pixel-scroll-precision-mode t))

;; ══ FONTS ═════════════════════════════════════════════════════════
(defun my/setup-fonts ()
  (set-face-attribute 'default nil
    :family "JetBrains Mono" :height 140 :weight 'regular)
  (set-face-attribute 'variable-pitch nil
    :family "SF Pro Text" :height 150)
  (set-fontset-font t 'emoji (font-spec :family "Apple Color Emoji")))
(add-hook 'emacs-startup-hook #'my/setup-fonts)
(add-hook 'server-after-make-frame-hook #'my/setup-fonts)

;; ══ EDITING ═══════════════════════════════════════════════════════
(setq-default indent-tabs-mode nil
              tab-width        4)
(electric-pair-mode     1)
(show-paren-mode        1)
(delete-selection-mode  1)
(global-auto-revert-mode 1)
(setq auto-revert-check-vc-info t)
(put 'narrow-to-region 'disabled nil)  ; enable narrowing
(put 'downcase-region  'disabled nil)  ; enable case commands
(global-set-key (kbd "M-/") #'hippie-expand)
(global-set-key (kbd "C-z") #'ignore)  ; disable accidental suspend
(global-set-key (kbd "C-c q") #'auto-fill-mode)

;; ══ BACKUPS ═══════════════════════════════════════════════════════
(let ((backup-dir (concat user-emacs-directory "backups/"))
      (auto-dir   (concat user-emacs-directory "auto-saves/")))
  (dolist (dir (list backup-dir auto-dir))
    (unless (file-exists-p dir) (make-directory dir t)))
  (setq backup-directory-alist         `(("." . ,backup-dir))
        auto-save-file-name-transforms `((".*" ,auto-dir t))
        backup-by-copying t
        kept-new-versions 8
        kept-old-versions 4
        delete-old-versions t))

;; ══ COMPLETION: VERTICO + ORDERLESS + CONSULT ════════════════════
(use-package vertico  :demand t :init (vertico-mode)
  :custom (vertico-cycle t))

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

(use-package marginalia :demand t :after vertico
  :init (marginalia-mode))

(use-package consult
  :bind (("C-x b"   . consult-buffer)
         ("C-x B"   . consult-buffer-other-window)
         ("M-y"     . consult-yank-pop)
         ("M-g g"   . consult-goto-line)
         ("C-s"     . consult-line)
         ("C-c f"   . consult-find)
         ("C-c G"   . consult-ripgrep)))

;; ══ COMPANY (IN-BUFFER COMPLETION) ══════════════════════════════
(use-package company
  :hook (prog-mode . company-mode)
  :custom
  (company-idle-delay           0.25)
  (company-minimum-prefix-length 2)
  (company-tooltip-limit        12))

;; ══ WHICH-KEY ════════════════════════════════════════════════════
(use-package which-key :demand t
  :init (which-key-mode)
  :custom (which-key-idle-delay 0.5))

;; ══ MAGIT ════════════════════════════════════════════════════════
(use-package magit
  :bind ("C-x g"   . magit-status)
        ("C-x M-g" . magit-dispatch)
  :custom (magit-display-buffer-function
           #'magit-display-buffer-same-window-except-diff-v1))

;; ══ EXEC-PATH-FROM-SHELL (MACOS ESSENTIAL) ═══════════════════════
(use-package exec-path-from-shell
  :demand t
  :if (memq window-system '(mac ns x))
  :config (exec-path-from-shell-initialize))

;; ══ THEME ════════════════════════════════════════════════════════
(use-package doom-themes :demand t
  :config (load-theme 'doom-one t))

(use-package doom-modeline :demand t
  :init (doom-modeline-mode 1)
  :custom (doom-modeline-height 28))

;; ══ ORG-MODE ═════════════════════════════════════════════════════
(use-package org
  :hook (org-mode . visual-line-mode)
        (org-mode . org-indent-mode)
  :custom
  (org-directory         "~/Documents/org")
  (org-default-notes-file (concat org-directory "/inbox.org"))
  (org-agenda-files      (list org-directory))
  (org-hide-emphasis-markers t)
  (org-startup-folded    'content)
  :bind (("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         ("C-c l" . org-store-link)))

;; ══ LINE NUMBERS & FILL COLUMN ═══════════════════════════════════
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
(add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)
(add-hook 'before-save-hook #'delete-trailing-whitespace)

;; ══ PERSONAL KEY PREFIX ══════════════════════════════════════════
(define-prefix-command 'my-personal-map)
(global-set-key (kbd "C-c p") 'my-personal-map)
(define-key my-personal-map (kbd "d") #'my/insert-date)
(define-key my-personal-map (kbd "t") #'my/open-terminal-here)
(define-key my-personal-map (kbd "T") #'my/toggle-theme)
(define-key my-personal-map (kbd "r") #'my/rename-file-and-buffer)

;; ══ END OF INIT.EL ═══════════════════════════════════════════════
(message "Emacs ready in %.2fs" (float-time (emacs-init-time)))
⚡ Next steps

From here, add language-specific packages as you need them: lsp-mode or eglot for LSP support; python-mode / rustic / web-mode for specific languages; yasnippet for text snippets; projectile or the built-in project.el for project management; and vterm for the best terminal experience. The config above is a solid, clean foundation to build from.