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 orgmode format to work place this snippet in init.el.

(require 'org)
  (expand-file-name ""

I do not keep theming stuff in my, because it's not workflow related. It just makes Emacs looks nicer.

Package repos

Setup repos

Debian 10 needs this variable to work.

(if my/home
    (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))

Now let's setup repos.

(require 'package)
(add-to-list 'package-archives
             '("melpa" . ""))
(unless package-archive-contents


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

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
      (require 'use-package))


Install Quelpa and Quelpa-Use-Package. Do not forget, that you have to have git installed on the system. Otherwise fetching will not work.

(setq quelpa-checkout-melpa-p nil) ;; use quelpa only for packages on MELPA

(unless (package-installed-p 'quelpa)
    (url-insert-file-contents "")

   :fetcher git
   :url ""))
(require 'quelpa-use-package)


What system are we running on?

At work I use Windows and at home GNU/Linux.

(defvar my/work (eq system-type 'windows-nt))
(defvar my/home (eq system-type 'gnu/linux))


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)

Unbind Arrow keys, let's try to learn default bindings.

(defun nope () (interactive) (message "Nope!"))

(dolist (key '("<left>" "<right>" "<up>" "<down>"))
  (define-key (current-global-map) (kbd key) 'nope))

Get backups out of the way and remove littering in auto-save-list

Disable backups and auto-save.

(setq auto-save-default nil)
(setq make-backup-files nil)

If not disabled, get them out of the way.

(setq auto-save-list-file-prefix nil)

(if my/work
    (setq temporary-file-directory "C:/Users/khajvaz/AppData/Local/Temp"))

(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

Helper functions and bindings

Automaticaly insert matching delimiters.

(electric-pair-mode 1)

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

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

Interactive call to revert-buffer. Ignoring the auto-save file and not requesting for confirmation. When the current buffer is modified, the command refuses to revert it, unless you specify the optional argument: force-reverting to true.

 (kbd "<f5>")
 (lambda (&optional force-reverting)
   (interactive "P")
   ;;(message "force-reverting value is %s" force-reverting)
   (if (or force-reverting (not (buffer-modified-p)))
       (revert-buffer :ignore-auto :noconfirm)
     (error "The buffer has been modified."))))



  • 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>") 'fd-switch-dictionary)
(global-set-key (kbd "<f10>") 'flyspell-check-next-highlighted-word)

(setq ispell-curent-dictionary "deutsch")
(setq ispell-local-dictionary "deutsch")

(if my/work
    (setq ispell-program-name "~/tools/hunspell/bin/hunspell.exe"))

(defun fd-switch-dictionary()
      (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"

Org Mode


(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)" "PROG(i/!)" "WAIT(w/!)" "|" "DONE(d/!)" "CANC(c/!)")))


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


I am using the package org-super-links and for preview I use the additional package org-super-links-peek.

(use-package org-super-links
  :quelpa (org-super-links :repo "toshism/org-super-links" :fetcher github)
  (("C-c s s" . org-super-links-link)
   ("C-c s l" . org-super-links-store-link)
   ("C-c s C-l" . org-super-links-insert-link)
   ("C-c s d" . org-super-links-delete-link))
  (setq org-super-links-related-into-drawer t))

(use-package org-super-links-peek
  :quelpa (org-super-links-peek :repo "toshism/org-super-links-peek" :fetcher github)
  :bind (("C-c s p" . org-super-links-peek-link)))


My templates for work and private stuff. Captured Tasks and Notes go in the Inbox and are refiled later.

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

(defun create-blog-post ()
          "Create an org file in ~/source/myblog/posts."
          (let ((name (read-string "Filename: ")))
            (expand-file-name (format "" name) "~/Nextcloud/Documents/blog/src/posts/")))

(setq org-capture-templates
      '(("t" "Task" entry (file "~/myorg/")
         "* TODO %?\n")
        ("n" "Note" entry (file "~/myorg/")
                 "* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n")
        ("p" "Post" plain (file create-blog-post)
         (file "~/myorg/assets/"))


Basic stuff

(define-key global-map "\C-ca" 'org-agenda)
(setq org-agenda-window-setup (quote current-window))
(setq org-agenda-files '("~/myorg/private/" "~/myorg/work/" "~/myorg/"))
(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 5)
(setq org-habit-preceding-days 14)
(setq org-habit-graph-column 189)
(setq org-agenda-use-time-grid nil)

Custom Agenda Views

  • Agenda idea taken from Aaron Bieber.
  • I mixed that up with the Eisenhower map of priority:
    • A = Urgent AND Important (DO NOW)
    • B = NOT Urgent and Important (DO)
    • C = Urgent and NOT Important (DELEGATE)
    • D = NOT Urgent AND NOT Important (DON'T DO)
  • To focus, I only show scheduled tasks from today. Every task here takes less than a portion of a work day to complete.
  • For stuff I need to work longer on, I use "PROG" state. This takes longer than a portion of a work day to complete.
  • Backlog contains everything that is still not being worked on ("PROG") or has not been schedueled for a specific day. I use priorities B to D in the backlog to sort it:
    • B: Prio 1, do this next when you have time.
    • C: Prio 2, do this at some time in future or delegate.
    • D: Everything else, maybe do not do this at all.
(setq org-highest-priority ?A)
(setq org-lowerst-priority ?D) 
(setq org-default-priority ?D)
(defun air-org-skip-subtree-if-priority (priority)
  (let ((subtree-end (save-excursion (org-end-of-subtree t)))
        (pri-value (* 1000 (- org-lowest-priority priority)))
        (pri-current (org-get-priority (thing-at-point 'line t))))
    (if (<= pri-value pri-current)

(setq org-agenda-custom-commands
      '(("k" "Agenda today for KEMAL"
         ((tags "PRIORITY<=\"A\""
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "DO NOW:")))
          (agenda ""
                  ((org-agenda-span 1)))
          (tags-todo "TODO=\"PROG\"" 
                     ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                      (org-agenda-overriding-header "Work on:")))
          (tags "CATEGORY=\"INBOX\"" 
                     ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                      (org-agenda-overriding-header "Refile:")))))
        ("i" "Backlog"
         ((alltodo ""
                   ((org-agenda-skip-function '(or (air-org-skip-subtree-if-priority ?A)
                                                   (org-agenda-skip-entry-if 'todo '("PROG"))
                                                   (org-agenda-skip-if nil '(scheduled deadline))))
                    (org-agenda-sorting-strategy '(priority-down effort-up))
                    (org-agenda-overriding-header "Backlog:")))))
        ;;; Some views for work, to filter stuff for specific recipients
        ("t" "Speak with the TEAM"
         ((tags "team"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ("b" "Speak with the BOSS"
         ((tags "boss"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ("w" "Speak with the SWPG"
         ((tags "swpg"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "Let's talk about:")))))
        ("j" "Topics for JFix"
         ((tags "jfix"
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                         (org-agenda-overriding-header "Let's talk about:")))))))


(setq additional-refile-targets '("~/myorg/private/" "~/myorg/work/" "~/myorg/work/" "~/myorg/work/"))

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


To use this make a screenshot with a tool that saves the picture to the clipboard. Then grab this photo from clipboard using org-download. Or install and use flameshot, so that you can edit the screenshot before inserting.

(use-package org-download
  :ensure t
  (setq org-download-method 'attach
        org-download-screenshot-method "flameshot gui --raw > %s")
  (if my/work
      (setq org-download-screenshot-method "imagemagick/convert"))
  (("C-c d i" . org-download-clipboard)
   ("C-c d s" . my-org-download-screenshot-wrapper)))

(defun my-org-download-screenshot-wrapper()
  ;; lower frame before taking the screenshot


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
   '((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
    (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))
          (org-publish-find-title entry project)
           (expand-file-name entry (plist-get (cdr project) :base-directory)))))

(defun rss-gen()
  ;; generate rss feed
   (delete '"blog.html" (directory-files "~/blog/pages/" nil
   :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
    (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
    (insert-pubdate "~/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" "~/blog/pages/" msg)))

Project setup

(setq org-publish-project-alist
         :base-directory "~/blog/src/posts/"
         :base-extension "org"
         :publishing-directory "~/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 ""
         :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=\"\">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= \"\">CC BY-SA 4.0</a></span> <span style=\"float: right;\"><a href= \"\">Discuss on Mastodon</a></span></p>")

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

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


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

(use-package which-key
  :ensure t


I find helm to be a nice package with a lot of functionality out-of-the-box that helps me work faster.

(use-package helm
  :ensure t
  (("M-x" . 'helm-M-x)
   ("C-x C-f" . 'helm-find-files)
   ("C-s" . 'helm-occur)
   ("C-q" . 'helm-org-ql)
   ("C-x C-b" . 'helm-buffers-list)
   ("M-y" . 'helm-show-kill-ring)
   ("C-x C-p" . 'helm-list-elisp-packages)
   ("C-x r b" . 'helm-bookmarks))
  (setq helm-autoresize-mode 0)
  (setq helm-split-window-in-side-p 1)
  (setq helm-move-to-line-cycle-in-source 1))

(use-package helm-org
  :ensure t)

(use-package helm-org-ql
  :ensure t)


Using this to chat on interessting channels like #emacs, #org-mode or #debian.

 erc-nick "anonimno")

(defun erc-liberachat ()
  (erc :server "" :port "6667"))

(require 'erc-services)
(erc-services-mode 1)


Using elfeed to read news from my Nextcloud.

(use-package elfeed
  :ensure t
  (global-set-key (kbd "C-x w") 'elfeed)
  (setq elfeed-use-curl t)
  (elfeed-set-timeout 36000)
  (setq elfeed-feeds '(
                         :use-authinfo t))))

(use-package elfeed-protocol
  :ensure t
  (setq elfeed-protocol-owncloud-maxsize 1000)
  (setq elfeed-protocol-owncloud-update-with-modified-time t)

(use-package elfeed-goodies
  :ensure t
  (setq elfeed-goodies/entry-pane-position 'bottom))

;; Some additional tweaking is needed on Windows.
(if my/work
     elfeed-curl-extra-arguments '("--insecure")
     auth-sources '((:source "c:/daten_lokal/.authinfo"))))

License: CC BY-SA 4.0 Discuss on Mastodon