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