My Emacs Configuration

1. My personal configuration

1.1. Design

I belive that one should explore what one has before finding something new. And so, why would one install packages for Emacs? sDASH.png


I've used it as my main editor for a while now, and have yet to find something I miss built-in to the editor and tool.

Let us take an example, magit and vc-mode. Magit is a popular git frontend for emacs, which many see as one of the best features of the tool. But why?

I have talked to multiple people that have never even looked at the built-in alternative, vc-mode.

In vc-mode I can easily stage files with one keybind, push, pull, stash, use git tags, don partial commits and more with a simple UI. Once someone said they wished for a way to browse the git log, but vc-mode has just this, and from here you can see, edit and browse the log and open the diffs from each commit.

This is just one of many examples of tooling in Emacs that many simply have never tried or even know of.


This is the base idea for my configuration. Use what I have and check what I have before I go looking for an external tool or package to solve a problem I may alreddy have a tool for avalable.

At the moment I have 0 external packages for my Emacs configuration, and if you use emacs version 30.1 or above you will have all that I use, where which-key is tyhe notable new feature.


If you want everything you may check my configuration over at *Codeberg*.

I have structured my configuration so that first i set the options, then i set values then last comes functions and keybinds. Like this:

(option 0/nil)
(setq option value)
(defun func (function))
(mode-set-key (kbd "keybind") 'function)

2. my configuration

First i require some shipped packages so that i am 100% sure they are loaded first thing, before the configurations are set (mostly for diary).

;;; minimal configuration
(require 'diary-lib)
(require 'rmail)
(require 'erc)

After that I set all the options that are toggles, like fido-vertical-mode and electric-pair-mode.

;;; basic opetions
(electric-pair-mode                1)
(fido-vertical-mode                1)
(repeat-mode                       1)
(which-key-mode                    1)
(save-place-mode                   1)
(global-hl-line-mode               1)
(savehist-mode                     1)
(recentf-mode                      1)
(global-auto-revert-mode           1)
(global-display-line-numbers-mode  1)
(column-number-mode                1)
(display-time-mode                 1)
(global-completion-preview-mode    1)
;;(etags-regen-mode                  1)

Here I just disable the visual clutter that is UI buttons.

;;; clean the UI
(tool-bar-mode   0)
(menu-bar-mode   0)
(scroll-bar-mode 0)

Then I set hooks, commands that are autoran on a condition, like for example startup or a specific mode.

;; autohooks
(add-hook  'before-save-hook              'delete-trailing-whitespace     )
(add-hook  'prog-mode-hook               #'prettify-symbols-mode         )
(add-hook  'org-mode-hook                #'prettify-symbols-mode         )
(add-hook  'scheme-mode-hook             #'imenu-add-menubar-index       )
(add-hook  'diary-fancy-display-hook      'diary-fancy-display    )
(add-hook  'calendar-mode-hook            'shrink-window-if-larger-than-buffer )
;;; binds: d - diary selected date, s: diary file, i d; insert diary,

Here i set it to pulse, so visually highlight the line for a moment on the specified commands.

;;; advice add
(advice-add 'find-file         :after #'my/pulse-line)
(advice-add 'switch-to-buffer  :after #'my/pulse-line)
(advice-add 'my/move-line-down :after #'my/pulse-line)
(advice-add 'my/move-line-up   :after #'my/pulse-line)

After that I start to set all the setq options. Here I make a better startupscreen and set how much emacs should remember back in time with actions like file naviagtion.

;;; remember what i did
(setq recentf-max-saved-items 100
      history-length          100
      kept-new-versions       10
      inhibit-splash-screen   t
      initial-scratch-message ""
      inhibit-startup-screen t

Then i set the diary file and for it to auto follow symlinks to vc managed directories for my startup screen.

;;; diary
      vc-follow-symlinks t
      diary-file "~/diary"
      calendar-date-style 'iso

And now I set where backupfiles are saved.

;;; set backups to .emacs.d and disable the bell
      ring-bell-function 'ignore
      display-line-numbers-type 'relative
      backup-directory-alist `(("." . ,user-emacs-directory))
      auto-save-file-name-transforms
      `((".*" ,user-emacs-directory t))
      auto-save-list-file-prefix
      user-emacs-directory

This is a simple `ERC` setup.

;;; irc
      epa-pinentry-mode 'loopback
      auth-sources '("~/.authinfo.gpg")
      erc-nick "Trondelag-Alice"
      erc-user-full-name "Trondelag"
      erc-autojoin-channels-alist '(("libera.chat" "#technicalrenaissance"))
      erc-use-auth-source-for-nickserv-password t
      erc-hide-list '("JOIN" "PART" "QUIT" "NICK" "MODE")
      erc-prompt-for-password nil

Options for xref and how it behaves, for M-./M-g i.

xref-show-definitions-function #'xref-show-definitions-completing-read
xref-show-xrefs-function       #'xref-show-definitions-completing-read
xref-auto-jump-to-first-definition 'unique

Simple rmail/email setup.

;;; email
       user-full-name "Alice"
       user-mail-address "trondelagcutie@yahoo.com"
       send-mail-function 'smtpmail-send-it
       smtpmail-smtp-server "smtp.mail.yahoo.com"
       smtpmail-smtp-service 465
       smtpmail-stream-type 'ssl
       rmail-movemail-program "/usr/local/bin/movemail"
       rmail-primary-inbox-list '("imaps://trondelagcutie%40yahoo.com@imap.mail.yahoo.com:993")

How emacs make backupfiles.

;;; how to backup
      backup-by-copying   t
      delete-old-versions t
      version-control     t
      create-lockfiles    nil
;;; CLOCK IN MODELINE FORMAT
      display-time-24hr-format t
      display-time-default-load-average nil

Y-N insted of yes-no.

;;; small options
      use-short-answers t
      tags-revert-without-query t

My completion style and how it shows up.

;;; completions
completion-styles '(basic partial-completion flex file-name)
      completions-format 'vertical
      completions-format 'one-column
      completions-max-height 8
      icomplete-scroll t
      tab-always-indent 'complete
      savehist-additional-variables '(kill-ring search-ring regexp-search-ring)

      completion-preview-minimum-symbol-length 2
      completion-preview-delay 0.02
      display-buffer-alist
      '(("\\*Completions\\*"
         (display-buffer-at-bottom)
         (window-height . 10)))

      completion-auto-select t
      completions-detailed t
      completions-max-height 10
      icomplete-prospects-height 10

      completions-group-format (propertize " %s " 'face '(:inherit shadow :slant italic))

      completions-group t
      read-file-name-completion-ignore-case t
      read-buffer-completion-ignore-case t

Enable symbol replacement for things like lambda to be shown as the lambda symbol.

;;; prettify the symbols
      prettify-symbols-unprettify-at-point t)

Set options to have smooth scrolling, show trailing whitespace.

;;; smoother scrolling
(setq-default scroll-conservatively 101
              scroll-margin 10
              indent-tabs-mode nil
              buffer-file-coding-system 'utf-8-auto-unix
              show-trailing-whitespace t
              imenu-auto-rescan t

Set what to show in the modeline.

;;; modeline
              header-line-format '("[ "display-time-string"] "
                                   "["vc-mode" ] " "[ %f ]")
              mode-line-format '("%e" "[%b] " "[%l:%c] "
                                 "["mode-name"] " " [%*] ")

Sets which symbols should be affected by prettify.

prettify-symbols-alist
'(("==" . "≡") ("===" . "≣") ("!=" . "≠") ("!==" . "≢")
  (">=" . "≥") ("<=" . "≤") ("->" . "→") ("<-" . "←")
  ("<->" . "↔") ("<=>" . "⇔") ("->>" . "↠") ("<<-" . "↞")
  ("~>" . "↝") ("|>" . "▷") ("<|" . "◁") ("map" . "↦")
  ("lambda" . "λ") ("alpha" . "α") ("beta" . "β") ("gamma" . "γ")
  ("delta" . "δ") ("pi" . "π") ("sum" . "∑") ("..." . "…")
  ("::" . "∷") (">>" . "»") ("<<" . "«") ("sqrt" . "√")
  ("integral" . "∫") ("forall" . "∀") ("exists" . "∃")))

Enable recursive minibuffers for completion in minibuffers and a keymap for it.

;;; setopts
(setopt enable-recursive-minibuffers t)

;;; eval after load
(with-eval-after-load 'completion-preview
  (define-key completion-preview-active-mode-map (kbd "M-n") #'completion-preview-next-candidate)
  (define-key completion-preview-active-mode-map (kbd "M-p") #'completion-preview-prev-candidate))

Function that highlights line for a moment.

;;; smal defun
(defun my/pulse-line (&rest _)
  (pulse-momentary-highlight-one-line (point)))

Function that moves selected line one line up.

;;; general functions
(defun my/move-line-up ()
  (interactive)
  (transpose-lines 1)
  (forward-line -2))

Function that moves line one line down.

(defun my/move-line-down ()
  (interactive)
  (forward-line 1)
  (transpose-lines 1)
  (forward-line -1))

A function that lets me have something like vims visual block mode for editing.

(defun my/visual-block-mode ()
  "something like vims visual block to a keybind, so i can edit multiple lines at once for the same thing"
  (interactive)
  (unless cua-mode   (cua-mode 1))
  (cua-rectangle-mark-mode 1))

Finds directories down and gives it to find-file wich has fuzzy search, so a fuzzy file finder, like ivy or fzf.

(defun my/find ()
  "fuzzy from current directory"
  (interactive)
  (let* ((files (directory-files-recursively default-directory ""))
         (chosen (completing-read "Find file: " files)))
    (when chosen
      (find-file chosen))))

This remakes the `TAGS` file.

(defun my/recompile-etags ()
  "a command to generate etags 'TAGS' file for the current projects"
  (interactive)
  (let ((default-directory (vc-root-dir)))
    (call-process-shell-command
     "find . -type f \\( -name '*.el' -o -name '*.[ch]' -o -name '*.scm' \\) -print0 | xargs -0 etags -o TAGS")))

A function that pushes current git repo withouth an ssh agent active.

(defun my/push ()
  "git push so it works on my machine and setup"
  (interactive)
  (let ((display-buffer-alist
         '(("\\*Async Shell Command\\*"
            display-buffer-no-window))))
    (shell-command "git push &"))
  (message "Pushing..."))

Here I simple set some keybinds to my functions and some built-ins that I use a lot.

;;; keybinds
(global-set-key (kbd "C-|"     ) 'my/visual-block-mode)
(global-set-key (kbd "C-c c s ")  'eshell             )
(global-set-key (kbd "C-c c g ")  'grep-find          )
(global-set-key (kbd "C-c c f ")  'my/find            )
(global-set-key (kbd "C-c c b ")  'ibuffer            )
(global-set-key (kbd "C-c c r ")  'recentf            )
(global-set-key (kbd "C-c C   ")  'compile            )
(global-set-key (kbd "C-c p   ")  'my/move-line-up    )
(global-set-key (kbd "C-c n   ")  'my/move-line-down  )
(global-set-key (kbd "C-c d   ")  'duplicate-line     )
(global-set-key (kbd "C-c c n ")  'narrow-to-region   )
(global-set-key (kbd "C-c c w ")  'widen              )
(global-set-key (kbd "C-c c P ")  'my/push            )

Sets move line functions to be repeatable.

(windmove-default-keybindings 'shift)
;;; repetemodeable
(defvar-keymap move-line-repeat-map
  "p" #'my/move-line-up
  "n" #'my/move-line-down)
(put #'my/move-line-down 'repeat-map 'move-line-repeat-map)
(put #'my/move-line-up   'repeat-map 'move-line-repeat-map)

Makes the base startup-screen the diary for the next week.

(defun my/start ()
  (diary 7)
  (delete-window))
(add-hook 'emacs-startup-hook 'my/start)

sCODE.png

Author: Trondelag (trondelagcutie@yahoo.com)

Date: 2026

Emacs 31.0.50 (Org mode 9.7.11)

Validate