Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 

Emacs Configuration

Introduction

Make sure that we are running a newer version of Emacs:

(when (version< emacs-version "27.1")
  (error "Unsupported version of Emacs"))

OS Detection

(setq fw/is-linux (eq system-type 'gnu/linux)
      fw/is-windows (eq system-type 'windows-nt))

General

Simplify confirmation:

(fset 'yes-or-no-p 'y-or-n-p)

Reload a file if it changed on disk:

(global-auto-revert-mode t)

Disable backup, auto save and lock files:

(setq backup-inhibited t
      auto-save-default nil
      create-lockfiles nil)

Disable audio bell:

(setq visible-bell t)

Hide startup message and show an empty scratch buffer:

(setq inhibit-startup-message t
      initial-scratch-message nil)

Increase the garbage collection threshold:

(setq gc-cons-threshold 20000000)

Always start in fullscreen:

(add-to-list 'initial-frame-alist '(fullscreen . maximized))

Make scrolling a little bit smoother:

(setq mouse-wheel-progressive-speed nil)

Server

Start Emacs in server mode:

(require 'server)
(unless (server-running-p) (server-start))

Text

Prefer UTF-8:

(prefer-coding-system 'utf-8)

Set default line length:

(setq-default fill-column 80)

Overwrite selected text when typing:

(delete-selection-mode t)

Ensure that files end with a new line:

(setq require-final-newline t)

Mark matching pairs of parentheses:

(show-paren-mode t)
(setq show-paren-delay 0.0)

Spaces > Tabs:

(setq-default indent-tabs-mode nil)

Use single space after a sentence:

(setq sentence-end-double-space nil)

Delete trailing whitespace on save:

(add-hook 'before-save-hook 'delete-trailing-whitespace)

Styling

Hide toolbar:

(tool-bar-mode 0)

Set the default font:

(set-face-attribute 'default nil
                    :family "Roboto Mono Medium"
                    :height 120
                    :weight 'normal
                    :width 'normal)

Enable column numbers:

(setq column-number-mode t)

Highlight current line:

(when window-system
  (global-hl-line-mode))

Ibuffer

(setq-default ibuffer-saved-filter-groups
              `(("Default"
                 ("Temporary" (name . "\*.*\*"))
                 ("Magit" (name . "^magit")))))

(add-hook 'ibuffer-mode-hook
          (lambda ()
            (ibuffer-auto-mode 1)
            (ibuffer-switch-to-saved-filter-groups "Default")))

(setq ibuffer-show-empty-filter-groups nil
      ibuffer-expert t)

Org

General org configuration:

(require 'org)

(setq org-catch-invisible-edits 'smart
      org-log-into-drawer t
      org-capture-bookmark nil)

Improve org’s source code blocks:

(setq org-src-fontify-natively t
      org-src-tab-acts-natively t
      org-src-window-setup 'current-window
      org-edit-src-content-indentation 0)

The calendar should use my native language. The calendar-set-date-style line changes the date format in %%(diary.anniversary ...):

(require 'calendar)

(calendar-set-date-style 'iso)

(setq calendar-week-start-day 1
      calendar-day-name-array ["Sonntag" "Montag" "Dienstag" "Mittwoch"
                               "Donnerstag" "Freitag" "Samstag"]
      calendar-month-name-array ["Jänner" "Februar" "März" "April"
                                 "Mai" "Juni" "Juli" "August" "September"
                                 "Oktober" "November" "Dezember"])

(setq parse-time-months '(("jän" . 1) ("feb" . 2) ("mär" . 3)
                          ("apr" . 4) ("mai" . 5) ("jun" . 6)
                          ("jul" . 7) ("aug" . 8) ("sep" . 9)
                          ("okt" . 10) ("nov" . 11) ("dez" . 12)
                          ("jänner" . 1) ("februar" . 2) ("märz" . 3)
                          ("april" . 4) ("mai" . 5) ("juni" . 6)
                          ("juli" . 7) ("august" . 8)
                          ("september" . 9) ("oktober" . 10)
                          ("november" . 11) ("dezember" . 12)))

(setq parse-time-weekdays '(("so" . 0) ("mo" . 1) ("di" . 2)
                            ("mi" . 3) ("do" . 4) ("fr" . 5)
                            ("sa" . 6) ("sonntag" . 0) ("montag" . 1)
                            ("dienstag" . 2) ("mittwoch" . 3)
                            ("donnerstag" . 4) ("freitag" . 5)
                            ("samstag" . 6)))

I’ve found this snippet of Austrian holidays in Karl Voit’s configuration:

(setq holiday-local-holidays
      '((holiday-fixed  1  1 "Neujahr (frei)")
        (holiday-fixed  1  6 "Heilige Drei Könige (frei)")
        (holiday-easter-etc 1 "Ostermontag (frei)")
        (holiday-easter-etc -46 "Aschermittwoch")
        (holiday-easter-etc -2 "Karfreitag")
        (holiday-fixed  5  1 "Österreichischer Staatsfeiertag (frei)")
        (holiday-easter-etc 39 "Christi Himmelfahrt (frei)")
        (holiday-easter-etc 50 "Pfingstmontag (frei)")
        (holiday-easter-etc 60 "Fronleichnam (frei)")
        (holiday-fixed  8 15 "Mariä Himmelfahrt (frei)")
        (holiday-fixed 10 26 "Nationalfeiertag (frei)")
        (holiday-fixed 11  1 "Allerheiligen (frei)")
        (holiday-fixed 12  8 "Maria Empfängnis (frei)")
        (holiday-fixed 12 24 "Heiliger Abend")
        (holiday-fixed 12 25 "Erster Weihnachtstag (frei)")
        (holiday-fixed 12 26 "Zweiter Weihnachtstag (frei)")))

(setq calendar-holidays (append holiday-local-holidays holiday-other-holidays))

Setup org-capture:

(defcustom fw/default-inbox "~/org/inbox.org"
  "The path to an org-mode file which is used when calling
  `fw/org-capture-inbox'")

(setq org-capture-templates
      '(("i" "Inbox" entry (file fw/default-inbox)
         "* %?" :empty-lines-before 1)))

(defun fw/org-capture-inbox ()
  "Opens my inbox `org-capture' template"
  (interactive)
  (org-capture nil "i"))

Basic agenda configuration with a custom agenda view:

(setq org-agenda-skip-scheduled-if-done t)

(setq org-agenda-custom-commands
      '(("." "Overview"
         ((agenda ""
                  ((org-agenda-overriding-header "Kalender\n")
                   (org-agenda-prefix-format "%-2i %-12:c%?-12t% s")
                   (org-agenda-scheduled-leaders '("" ""))))
          (todo ""
                ((org-agenda-overriding-header "\nOffen\n")
                 (org-agenda-block-separator nil)
                 (org-agenda-sorting-strategy '(todo-state-up))
                 (org-agenda-todo-ignore-deadlines 'all)
                 (org-agenda-todo-ignore-scheduled 'all)))))))

(defun fw/org-agenda ()
  "Show my custom org-agenda."
  (interactive)
  (delete-other-windows)
  (find-file fw/default-inbox)
  (org-agenda nil "."))

I don’t want to see repeating TODOs in my calendar:

(setq org-agenda-show-future-repeats nil)

Define a function to make my org-agenda pretty. Based on this reddit post:

(defun fw/org-agenda-category-icon (category icon)
  "Returns an `org-agenda-category-icon-alist' entry using an
`all-the-icons-material' icon"
  `(,category ,(list (all-the-icons-material icon)) nil nil :ascent center))

External Packages

Themes

I like to use light themes:

(load-theme 'sanityinc-tomorrow-day t)

with just some minor adjustments:

(set-face-attribute 'org-agenda-structure nil :height 1.25)
(set-face-attribute 'org-agenda-date-today nil :slant 'normal)

Markdown

(autoload 'gfm-mode "markdown-mode"
  "Major mode for editing GitHub Flavored Markdown files" t)

(add-to-list 'auto-mode-alist '("\\.md\\'" . gfm-mode))

Magit

(require 'magit)

(setq git-commit-summary-max-length 50
      git-commit-fill-column 72
      magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)

I’d like to spellcheck my commit messages:

(when (executable-find "aspell")
  (add-hook 'git-commit-mode-hook 'flyspell-mode))

Running magit-status with a prefix argument shows all repositories defined in magit-repository-directories, even if I am already in a git repository. Since I don’t like to press C-u, I’ll define a function to do it for me:

(defun fw/prefix-magit-status ()
  "Runs C-u `magit-status'"
  (interactive)
  (setq current-prefix-arg '(4))
  (call-interactively 'magit-status))

Define a function to open the current/root directory in an external file manager:

(defun fw/default-or-root-dir ()
  "Returns `magit-toplevel' or `default-directory'."
  (let ((root-dir (magit-toplevel)))
    (expand-file-name
     (if root-dir
         root-dir
       default-directory))))

(defun fw/native-file-manager ()
  "Opens the OS native file manager in `fw/default-or-root-dir'."
  (interactive)
  (when fw/is-linux
    (call-process "xdg-open" nil 0 nil (fw/default-or-root-dir)))
  (when fw/is-windows
    (w32-shell-execute "open" (fw/default-or-root-dir))))

Use magit so that consult can identify git projects:

(setq consult-project-root-function #'magit-toplevel)

Elfeed

Let’s extend elfeed’s UI so that I can use youtube-dl to download RSS video feeds. This snippet is based on code I found here and here:

(require 'elfeed)

(defcustom fw/youtube-dl-program "youtube-dl"
  "The default youtube-dl program")

(defcustom fw/youtube-dl-args ""
  "A string containing command line arguments which are passed to
  youtube-dl")

(defun fw/youtube-dl (url)
  "Downloads a URL using youtube-dl"
  (async-shell-command (concat fw/youtube-dl-program " " fw/youtube-dl-args " " url)
                       (concat "*youtube-dl " url "*")))

(defun fw/youtube-dl-prompt ()
  "Prompts the user for an URL to download using youtube-dl"
  (interactive)
  (fw/youtube-dl (read-string "YouTube URL: ")))

(defun fw/elfeed-search-youtube-dl ()
  "Downloads an elfeed entry using youtube-dl"
  (interactive)
  (let ((entries (elfeed-search-selected)))
    (dolist (entry entries)
      (fw/youtube-dl (elfeed-entry-link entry))
      (elfeed-untag entry 'unread)
      (elfeed-search-update-entry entry)
      (unless (use-region-p) (forward-line)))))

(define-key elfeed-show-mode-map "d" 'fw/elfeed-search-youtube-dl)
(define-key elfeed-search-mode-map "d" 'fw/elfeed-search-youtube-dl)

Vertico & Orderless

(vertico-mode)

(define-key vertico-map "\r" #'vertico-directory-enter)
(define-key vertico-map "\d" #'vertico-directory-delete-char)
(define-key vertico-map "\M-\d" #'vertico-directory-delete-word)

(require 'orderless)
(setq completion-styles '(orderless))

Embark

(when fw/is-linux
  (global-set-key (kbd "M-<menu>") 'embark-act))

(when fw/is-windows
  (global-set-key (kbd "M-<apps>") 'embark-act))

(require 'embark-consult)

Company

(setq company-idle-delay 0.1
      company-minimum-prefix-length 3
      company-show-numbers t)

(global-company-mode t)

The dabbrev backend has some inconvenient default settings (e.g. its suggestions get downcased, even if notations such as camel casing are used):

(setq company-dabbrev-downcase nil
      company-dabbrev-ignore-case nil)

Doom Modeline

This modeline uses all-the-icons, which can be installed by running the command M-x all-the-icons-install-fonts.

(doom-modeline-mode 1)

Do not show method names in the modeline:

(setq which-func-modes nil)

PowerShell

(add-to-list 'auto-mode-alist '("\\.psm1\\'" . powershell-mode))
(add-to-list 'auto-mode-alist '("\\.psd1\\'" . powershell-mode))

C Sharp

(add-to-list 'auto-mode-alist '("\\.csproj\\'" . nxml-mode))
(add-to-list 'auto-mode-alist '("\\.props\\'" . nxml-mode))
(add-to-list 'auto-mode-alist '("\\.editorconfig\\'" . conf-mode))

Compilation

I have excluded *.elc files in this git repository, which is why I need to make sure that all packages are compiled:

(byte-recompile-directory package-user-dir 0)

Keybindings

A post by Xah Lee gave me the idea to create alternative keybindings using transients and the apps/menu key:

(defun fw/save ()
  "Runs the keybinding C-x C-s"
  (interactive)
  (funcall (key-binding (kbd "C-x C-s"))))

(defun fw/org-edit ()
  "Runs the keybinding C-c '"
  (interactive)
  (funcall (key-binding (kbd "C-c '"))))

(defun fw/consult-line-at-point ()
  "Calls `consult-line' using `thing-at-point'"
  (interactive)
  (consult-line (thing-at-point 'symbol)))

(defun fw/consult-ripgrep-at-point ()
  "Calls `consult-ripgrep' using `thing-at-point'"
  (interactive)
  (consult-ripgrep nil (thing-at-point 'symbol)))

(transient-define-prefix fw/transient-main ()
  "fw/transient-main"
  [["Search"
    ("f" "Find file" find-file)
    ("F" "Find file recursive" consult-find)
    ("s" "Search buffer" consult-line)
    ("S" "Search directory" consult-ripgrep)
    ("j" "Goto line" consult-goto-line)]

   ["Buffer"
    ("w" "Save buffer" fw/save)
    ("k" "Kill buffer" kill-this-buffer)
    ("b" "Switch buffer" consult-buffer)
    ("B" "Open ibuffer" ibuffer)
    ("h" "Mark all" mark-whole-buffer)
    ("SPC" "Set mark" set-mark-command)]

   ["Window"
    ("0" "Delete window" delete-window)
    ("1" "Delete other windows" delete-other-windows)
    ("2" "Split window below" split-window-vertically)
    ("3" "Split window right" split-window-horizontally)
    ("<right>" "Right" windmove-right)
    ("<left>" "Left" windmove-left)
    ("<up>" "Up" windmove-up)
    ("<down>" "Down" windmove-down)]

   ["More"
    ("r" "Rectangle" fw/transient-rectangle :transient nil)
    ("g" "Project" fw/transient-project :transient nil)
    ("e" "Elfeed" elfeed)
    ("o" "Org" fw/transient-org :transient nil)
    ("t" "Text" fw/transient-text :transient nil)
    ("." "Point" fw/transient-at-point :transient nil)
    ("y" "Yank" consult-yank-pop)
    ("<return>" "Execute" execute-extended-command)]

   ["Quit"
    ("q" "Quit Emacs" save-buffers-kill-terminal)]])

(transient-define-prefix fw/transient-rectangle ()
  "fw/transient-rectangle"
  [["Rectangle"
    ("r" "Mark" rectangle-mark-mode)
    ("i" "Insert" string-rectangle)
    ("d" "Delete" delete-rectangle)]])

(transient-define-prefix fw/transient-project ()
  "fw/transient-project"
  [["Magit"
    ("g" "Status" magit-status)
    ("o" "Open repository" fw/prefix-magit-status)
    ("d" "File dispatch" magit-file-dispatch)]

   ["OS"
    ("e" "File manager" fw/native-file-manager)]])

(transient-define-prefix fw/transient-org ()
  "fw/transient-org"
  [["Org"
    ("o" "Agenda" fw/org-agenda)
    ("i" "Inbox" fw/org-capture-inbox)
    ("l" "Link" org-insert-link)
    ("t" "Todo" org-todo)
    ("s" "Schedule" org-schedule)
    ("d" "Deadline" org-deadline)
    ("." "Timestamp" org-time-stamp)
    ("#" "Template" org-insert-structure-template)
    ("b" "Source" fw/org-edit)
    ("B" "Babel" org-babel-tangle)]])

(transient-define-prefix fw/transient-text ()
  "fw/transient-text"
  [["Spelling"
    ("t" "Check word" ispell-word)
    ("f" "Flyspell mode" flyspell-mode)
    ("b" "Flyspell buffer" flyspell-buffer)]

   ["Layout"
    ("w" "Whitespace mode" whitespace-mode)]])

(transient-define-prefix fw/transient-at-point ()
  "fw/transient-at-point"
  [["Search"
    ("s" "Search buffer" fw/consult-line-at-point)
    ("S" "Search directory" fw/consult-ripgrep-at-point)]

   ["Highlight"
    ("h" "Highlight" highlight-symbol-at-point)
    ("u" "Clear" unhighlight-regexp)]])

(global-set-key (kbd "C-z") 'undo)
(global-set-key (kbd "<f5>") 'fw/transient-main)

(when fw/is-linux
  (global-set-key (kbd "<menu>") 'fw/transient-main))

(when fw/is-windows
  (global-set-key (kbd "<apps>") 'fw/transient-main))

Change all transients so that I can use q to close them:

(transient-bind-q-to-quit)

Custom

Additional configuration that is only relevant on a particular machine should be stored in a custom.el file:

(let ((fw/custom-el (concat user-emacs-directory "custom.el")))
  (when (file-exists-p fw/custom-el)
    (load-file fw/custom-el)))

About

My custom Emacs configuration

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published