My Emacs Config

One config to rule them all

This is my Emacs config for home and work.
Feel free to borrow, copy and hack … I did it myself from different sources!
Hint: To edit lisp blocks use C-c '

Setup of init.el

To get this initialization in org-mode format to work place this snippet in init.el.

(require 'org)
  (org-babel-load-file
  (expand-file-name "config.org"
                    "~/myorg/assets"))

I do not keep theming stuff in my config.org, because it's not workflow related. It just makes Emacs looks nicer.
But there are some formating changes I like to have in all themes, this is then added to a tweaks theme I load separately.

(add-to-list 'custom-theme-load-path "~/myorg/assets/")

Package repos

Setup repos

Let's setup standard repos, nothing exotic here.

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/"))
(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

Use-Package

Install Use-Package, then use it for other packages.

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(eval-when-compile
      (require 'use-package))

Basics

Interface

Get the interface cleaner.

(setq inhibit-startup-screen t)
(setq inhibit-startup-echo-area-message t)
(setq inhibit-startup-message t)   
(tool-bar-mode 0)
(tooltip-mode  0)
(menu-bar-mode 0)
(scroll-bar-mode 0)
(defalias 'yes-or-no-p 'y-or-n-p)
(global-visual-line-mode 1)
(global-hl-line-mode 1)
;;(setq initial-major-mode 'org-mode)
;;(setq initial-scratch-message nil)
;; Always start maximized and split window vertically
(add-to-list 'default-frame-alist '(fullscreen . maximized))
(setq split-height-threshold 0)
(setq split-width-threshold 0)

Backups

Turn the backups on, but get them out of the way and into temporary directory.

(setq auto-save-default t)
(setq make-backup-files t)
(setq auto-save-list-file-prefix nil)
(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

Delimiters

Turn on electric mode, to get automatic pairs.

(electric-pair-mode 1)

Bookmarks

Custom Bookmarks, so that they are same on all machines. Do not forget running bookmarks-save after editing bookmarks.

(setq bookmark-default-file "~/myorg/assets/mybookmarks")

Calendar

Add CW to the calender and set monday as the first day in the week.

(define-key global-map "\C-xc" 'calendar)
(setq calendar-week-start-day 1)
(add-hook 'calendar-load-hook
              (lambda ()
                (calendar-set-date-style 'european)))
(copy-face font-lock-constant-face 'calendar-iso-week-face)
(set-face-attribute 'calendar-iso-week-face nil
                    :height 1.0)
(setq calendar-intermonth-text
      '(propertize
        (format "%2d"
                (car
                 (calendar-iso-from-absolute
                  (calendar-absolute-from-gregorian (list month day year)))))
        'font-lock-face 'calendar-iso-week-face))

Spelling

Usage:

  • Use f11 to set dictionary.
  • Press f12 to check spelling in the buffer.
  • Press f10 to go to the next spelling error, ispell shows corrections that be chosen. If not needed skip with SPC.
  • Also if you set the cursor on the spelling error you can use f10 to correct it.
  • If using a mouse, choose the word with the wheel. Then you get correction proposals.
(global-set-key (kbd "<f12>") 'flyspell-buffer)
(global-set-key (kbd "<f11>") 'flyspell-check-next-highlighted-word)
(global-set-key (kbd "<f10>") 'fd-switch-dictionary)

(setq ispell-program-name "aspell")
(setq ispell-local-dictionary "deutsch")
(setq ispell-current-dictionary "deutsch")

(defun fd-switch-dictionary()
  (interactive)
  (let* ((dic ispell-current-dictionary)
         (change (if (string= dic "deutsch") "english" "deutsch")))
    (ispell-change-dictionary change)
    (message "Dictionary switched from %s to %s" dic change)))

(defun flyspell-check-next-highlighted-word ()
  "Custom function to spell check next highlighted word"
  (interactive)
  (flyspell-goto-next-error)
  (ispell-word))

Org Mode

Basics

Just some basic setup for Org Mode. A lot of is self-explanatory.

(setq org-directory "~/myorg")
(add-to-list 'org-modules 'org-habit t)
(setq org-startup-indented t)
(setq org-hide-emphasis-markers t)
(setq org-startup-folded t)
(setq org-log-into-drawer t)
(setq org-todo-keywords
      '((sequence "TODO(t/!)" "WAIT(w/!)" "|" "DONE(d/!)" "CANC(c/!)")))
(setq org-todo-keyword-faces
 '(("TODO" . "red") ("WAIT" . "magenta") ("CANC" . "grey") ("DONE" . "darkgreen")))
(setq org-duration-format (quote h:mm))
(setq org-use-tag-inheritance nil)

File handling

Open links in external applications.

(setq org-file-apps
   '((auto-mode . emacs)
     ("\\.mm\\'" . default)
     ("\\.x?html?\\'" . default)
     ("\\.pdf\\'" . "okular %s")
     ("xlsx" . "libreoffice %s")
     ("pptx" . "libreoffice %s")
     ("odp" . "libreoffice %s")
     ("ods" . "libreoffice %s")))

Linking

Generate IDs, so that the links to do not brake.

(require 'org-id)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
(define-key global-map "\C-cl" 'org-store-link)

Capture

Captured Tasks and Notes go in the Inbox and are refiled later.
Also a template for clocking my work time, creating simple documents and presenatations.

(define-key global-map (kbd "C-c c") 'org-capture)

(defun create-blog-post ()
          "Create an org file in blog/src/posts."
          (interactive)
          (let ((name (read-string "Filename: ")))
            (expand-file-name (format "%s.org" name) "~/myorg/blog/src/posts/")))

(defun create-document ()
          "Create an org file in ~/myorg/docs"
          (interactive)
          (let ((name (read-string "Filename: ")))
            (expand-file-name (format "%s.org" name) "~/myorg/docs/")))

(defun create-presentation ()
              "Create an org file in ~/myorg/docs"
              (interactive)
              (let ((name (read-string "Filename: ")))
                (expand-file-name (format "%s.org" name) "~/myorg/docs/")))

(setq org-capture-templates
      '(("t" "Task" entry (file "~/myorg/inbox.org")
         "* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n")
        ("n" "Note" entry (file "~/myorg/inbox.org")
                 "* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n")
        ("p" "Post" plain (file create-blog-post)
         (file "~/myorg/assets/post_template.org"))
        ("d" "Document" plain (file create-document)
         (file "~/myorg/assets/doc_template.org"))
        ("r" "Presentation" plain (file create-document)
         (file "~/myorg/assets/pres_template.org"))
        ("z" "Clock In" entry (file+headline "~/myorg/work.org" "Stempelzeiten")
         "* %u\n"
         :clock-in t :clock-keep t :immediate-finish t)
        ))

Agenda

Basic stuff

(define-key global-map "\C-ca" 'org-agenda)
(define-key global-map "\C-cr" 'org-revert-all-org-buffers)
(setq org-agenda-window-setup (quote current-window))
(setq org-agenda-files '("~/myorg/private.org" "~/myorg/work.org" "~/myorg/inbox.org"))
(setq org-agenda-start-day nil)
(setq org-agenda-span 7)
(setq org-agenda-start-on-weekday nil)
(setq org-agenda-skip-deadline-prewarning-if-scheduled t)
(setq org-agenda-todo-ignore-deadlines (quote all))
(setq org-agenda-todo-ignore-scheduled (quote all))
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-deadline-if-done t)
(setq org-deadline-warning-days 0)
(setq org-habit-preceding-days 14)
(setq org-habit-graph-column 189)
(setq org-agenda-use-time-grid nil)
(setq org-stuck-projects '("TODO={.+}/-DONE-CANC" nil ("MAYBE")  "SCHEDULED:\\|DEADLINE:"))

Custom Agenda Views

  • To focus, I only show scheduled tasks from today. Every task here takes less than a portion of a work day to complete.
  • For employees, I defined name tags. To filter out these tasks from my One-on-One with them, I match those tags in the agenda.
(setq org-agenda-custom-commands
      '(("k" "Agenda today for KEMAL"
         ((agenda ""
                  ((org-agenda-span 1)))
          (tags "CATEGORY=\"INBOX\"" 
                     ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                      (org-agenda-overriding-header "Refile:")))
          ))
        ;;; Some views for work, to filter stuff for specific recipients
        ("w" "Topics for Team Weekly"
         ((tags "weekly"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ("b" "BOSS related tasks"
         ((tags "boss"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ("j" "Topics for Jour Fix"
         ((tags "jfix"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ))

Refile

(setq org-refile-targets
      '((nil :maxlevel . 5)
        (org-agenda-files :maxlevel . 5)))
(setq org-refile-use-outline-path t)
(setq org-outline-path-complete-in-steps nil)

Blog

Get needed packages.

(require 'ox-html)
(require 'ox-publish)
(use-package webfeeder
  :ensure t)
(use-package htmlize
  :ensure t)
(use-package lua-mode
  :ensure t
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((lua . t))))

Helper stuff

Some stuff from Pavel Panchekha to clear up HTML and additional stuff for HTML5 addaptation to simple.css.

(setq org-html-home/up-format "")
(setq org-html-link-up "")
(setq org-html-link-home "")
(setq org-html-scripts "")
(setq org-html-doctype "html5")
(setq org-html-html5-fancy t)
(setq org-html-container-element "main")

Get stuff for the sitemap, generate RSS, publish.

(defun org-find-category (file)
  ;; find category in the posts
  (with-temp-buffer
    (message file)
    (insert-file-contents file)
    (goto-char (point-min))
    (let ((beg (+ 1 (re-search-forward "^#\\+CATEGORY\:")))
          (end (progn (forward-word) (point))))
      (buffer-substring beg end))))

(defun my-sitemap-format-entry (entry style project)
  ;; format the entries in the sitemap
  (format "%s - [[file:%s][%s]] (%s)"
          (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))
          entry
          (org-publish-find-title entry project)
          (org-find-category
           (expand-file-name entry (plist-get (cdr project) :base-directory)))))

(defun rss-gen()
  ;; generate rss feed
  (interactive)
  (webfeeder-build
   "rss.xml"
   "~/myorg/blog/pages/"
   "https://anonimno.codeberg.page/"
   (delete '"blog.html" (directory-files "~/myorg/blog/pages/" nil
                                          "\.html"))
   :title "anonimno's blog"
   :description "My blog in RSS"
   :builder 'webfeeder-make-rss))

(defun insert-pubdate (file)
  ;; insert current date as pubdate in the generated sitemap
  (interactive)
  (with-temp-buffer
    (message file)
    (insert-file-contents file)
    (goto-char (point-min))
    (re-search-forward "hidden;\">")
    (insert (shell-command-to-string "echo -n $(date +%Y-%m-%d)"))
    (write-file file)))

(defun my-blog-publish()
  ;; publish the posts
  (interactive)
  (progn
    (org-publish-all)
    (rss-gen)
    (insert-pubdate "~/myorg/blog/pages/blog.html") ;; insert pubdate in sitemap
    ;;(let ((msg (read-string "Commit msg: "))))
    (let ((msg (read-string "Commit msg: ")))
      (shell-command (format "%s %s" "~/myorg/blog/pages/git_publish.sh" msg)))
    ))

Publish setup

(setq org-publish-project-alist
      '(("blog"
         :base-directory "~/myorg/blog/src/posts/"
         :base-extension "org"
         :publishing-directory "~/myorg/blog/pages/"
         :publishing-function org-html-publish-to-html
         :recursive t
         :exclude "level-.*\\|.*\.draft\.org"
         :section-numbers nil
         :with-toc nil
         :with-sub-superscript nil
         :with-author nil
         :with-date t
         :with-drawers t
         :auto-sitemap t
         :sitemap-filename "blog.org"
         :sitemap-title "Posts"
         :sitemap-sort-files anti-chronologically
         :sitemap-style list
         :sitemap-format-entry my-sitemap-format-entry
         :html-head-include-default-style nil
         :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"assets/site.css\" />"
         :html-preamble "<nav> <a href=\"/index.html\">Home</a> <a href=\"/blog.html\">Blog</a> <a href=\"https://codeberg.org/anonimno\">Codeberg</a> <a href=\"/blogroll.html\">Blogroll</a> <a href=\"/contact.html\">Contact</a> <a href=\"/rss.xml\">RSS</a> </nav><p class=\"pubdate\" style=\"visibility:hidden;\"> %d </p>"
         :html-postamble "<p><span class=\"date\" style=\"float: left;\">License: <a href= \"https://creativecommons.org/licenses/by-sa/4.0/\">CC BY-SA 4.0</a></span> <span style=\"float: right;\"><a href= \"https://social.tchncs.de/web/accounts/310111\">Discuss on Mastodon</a></span></p>")

        ("assets"
         :base-directory "~/myorg/blog/src/assets/"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|webp"
         :publishing-directory "~/myorg/blog/pages/assets/"
         :recursive t
         :publishing-function org-publish-attachment)

        ("all" :components ("blog" "assets"))))

Which-Key

Let's get those keybindings shown, because I tend to forget them.

(use-package which-key
  :ensure t
  :init
  (which-key-mode))

Yankpad

Yankpad for snippets insertion.

(use-package yankpad
  :ensure t
  :init
  (setq yankpad-file "~/myorg/assets/yankpad.org")
  :config
  (bind-key "C-x y" 'yankpad-insert))

Vertico and friends

Taken from System Crafters.

(use-package vertico
  :ensure t
  :custom
  (vertico-cycle t)
  (vertico-count 15)
  (vertico-resize nil)
  :init
  (vertico-mode))

(use-package savehist
  :init
  (savehist-mode))

(use-package marginalia
  :after vertico
  :ensure t
  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  (marginalia-align 'right)
  :init
  (marginalia-mode))

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

Org-ql

I use for all my stuff just two "big" Org files, org-ql helps finding stuff in those files.

(use-package org-ql
  :ensure t
  :config
  (bind-key "C-c f" 'org-ql-find)
  (bind-key "C-c s" 'org-ql-search))

License: CC BY-SA 4.0 Discuss on Mastodon