- Emacs Lisp 100%
| bookmarks | Update bookmarks and custom-vars.el with current files in .emacs.d on my main machine. | |
| config.org | Remove RSS feed for Archlinux News. I use Guix for rolling and Debian for fixed release now. | |
| custom-vars.el | Remove einar-systemcrafters function, remove rcirc settings for it in custom-vars.el. | |
| eww-bookmarks | Add r/emacs to RSS feeds. | |
| init.el | Updated paths to Python on Windows (also for the REPL) and to texlive (now in Chocolatey). | |
| LICENSE | Changed the LICENSE file to the actual GPLv3. | |
| nov-places | Add .mkv to browse-url-handler with einar-browse-url-mpv. I now use W, not & in dired to play media files... Also fixed type of eww-header-line-format from 'nil to "". | |
| README.org | Make README.org a link to config.org. | |
Einar Mostad's Emacs Configuration
- Overview
- My functions
- Keybindings, general UI and UX, window management
- Package repositories
- Spelling (flyspell, ispell)
- Dictionary lookup (not spell checking)
- Bookmarks
- Diff without font lock on green/red background
- Keyboard shortcut exploration (which-key)
- Shells
- Org Mode, presentations and blog
- Templates
- Programming
- Tab-key for indentation and completion
- Indent with spaces (but press tab to get them)
- 80 column width (auto-fill-mode)
- Dumb-jump to jump to definitions
- Colour codes (Rainbow mode)
- Highlight changes in the margin in git repos
- Web mode for HTML, CSS, PHP, .aspx etc
- JavaScript indentation at 2 spaces
- Python
- (Bash) Shell
- Clojure
- Document viewer (Reader mode)
- Web Browser (EWW)
- Calendar, Diary and World Clock
- File Manager (dired, image-dired, dired-async)
- RSS reader (Elfeed)
- Music (mpc mode)
- Email (mu4e)
- Ebooks (Nov mode)
- Process editor
- Copyright and license
Overview
This is my literate config which I tangle and load from init.el.
My functions
I use Emacs functions to avoid external dependencies and shell scripts. They are bound to keys in Emacs and Sway. Their docstrings explain what they do.
(defun einar-mount-toggle ()
"Asks for a parition to mount at ~/mount if none is or unmounts ~/mount."
(interactive)
(if (equal '("." "..") (directory-files "/home/einar/mount"))
(let* ((partisjoner (split-string (shell-command-to-string "lsblk -lp | grep 'part' | awk '{print 1,ドル 4ドル}'") "\n"))
(partisjon (car (split-string (completing-read "Velg en partisjon å legge til filsystemet: " partisjoner nil t) " "))))
(async-shell-command (concat "sudo mount " partisjon " /home/einar/mount")))
(async-shell-command "sudo umount ~/mount")))
(defun einar-bgshuffle ()
"Sets a random wallpaper from ~/Bilder/Skrivebordsbakgrunner/ on Sway."
(interactive)
(let* ((filer (nthcdr 2 (directory-files
"/home/einar/Bilder/Skrivebordsbakgrunner/" t)))
(antall (length filer))
(nummer (random antall))
(nyfil (nth nummer filer)))
(shell-command (concat "ln -fsT " nyfil " /home/einar/.config/bakgrunn/bakgrunn"))
(setq wallpaper-command "swaymsg")
(setq wallpaper-command-args '("output" "'*'" "bg" "%f" "fill"))
(wallpaper-set nyfil)))
(defun einar-nestebg ()
"Sets the next file in ~/Bilder/Skrivebordsbakgrunner/ as the wallpaper on
Sway or if using the last file as wallpaper, wrap around to the first image."
(interactive)
(let* ((filnavn (file-symlink-p "/home/einar/.config/bakgrunn/bakgrunn"))
(filer (nthcdr 2 (directory-files
"/home/einar/Bilder/Skrivebordsbakgrunner/" t)))
(antall (length filer))
(nummer (cl-position filnavn filer :test 'equal))
(nyfil (if (= nummer (- antall 1)) (nth 0 filer) (nth (+ 1 nummer) filer))))
(shell-command (concat "ln -fsT " nyfil " /home/einar/.config/bakgrunn/bakgrunn"))
(setq wallpaper-command "swaymsg")
(setq wallpaper-command-args '("output" "'*'" "bg" "%f" "fill"))
(wallpaper-set nyfil)))
(defun einar-launch ()
"Makes an Emacs frame with a minibuffer that runs async-shell-command
as a launcher. Also sets aside some room for the completion buffer."
(interactive)
(with-selected-frame (make-frame '((name . "einar-launch")
(width . 116)
(height . 6)))
(unwind-protect
(progn
(switch-to-buffer "launcher")
(setq mode-line-format nil)
(call-interactively 'async-shell-command))
(delete-frame))))
(defun einar-side-window-side ()
"Sets some buffers to pop up in side windows. Sets side for side-windows
based on whether the frame is in portrait or landscape orientation."
(interactive)
(let* ((side-window-side (if (> (frame-outer-height) (frame-outer-width))
'bottom 'right))
(disp-buf-alist `(("\\*\\(Python\\|ielm\\|cider-repl\\|compilation\\).*\\*"
(display-buffer-reuse-window display-buffer-in-side-window)
(side . ,side-window-side)
(slot . -1)
(post-command-select-window . t)
(window-width . 80))
("\\*\\(shell\\|.*eshell\\).*\\*"
(display-buffer-reuse-window display-buffer-in-side-window)
(side . ,side-window-side)
(slot . 0)
(post-command-select-window . t)
(window-width . 80))
("\\*\\(help\\|info\\|Guix Packages\\|vc-git\\|man\\|woman\\|eldoc\\|EXIF\\|Agenda Commands\\|Org Agenda\\|Occur\\|Buffer.\\|xref\\).*\\*"
(display-buffer-reuse-window display-buffer-in-side-window)
(side . ,side-window-side)
(slot . 1)
;(post-command-select-window . t) ;Ideally nil for EXIF, t otherwise.
(window-width . 80)))))
(setq display-buffer-alist disp-buf-alist)))
(defun einar-photo-metadata (&rest _)
"Show EXIF metadata for an image in a side window (see einar-side-window-side)
when in image-mode."
(interactive)
(save-current-buffer
(with-output-to-temp-buffer "*EXIF*"
(princ (shell-command-to-string (concat "exiftool -ModifyDate -Make -Model -LensType -FocalLength -ExposureTime -FNumber -ISO -Flash -ExposureProgram -ExposureCompensation -WhiteBalance -MeteringMode -FocusMode -PhotoStyle -DigitalZoomRatio -ImageSize -Megapixels '" (buffer-file-name) "'"))))))
(defun einar-toggle-photo-metadata ()
"Toggles showing EXIF metadata for images in a side window on or off."
(interactive)
(if (member 'einar-photo-metadata image-mode-new-window-functions)
(progn
(remove-hook 'image-mode-new-window-functions
'einar-photo-metadata)
(if (get-buffer "*EXIF*")
(kill-buffer "*EXIF*")))
(add-hook 'image-mode-new-window-functions
'einar-photo-metadata)))
(defun einar-python-virtualenv ()
"Makes the python shell in Emacs find packages from the virtual environment
if the file it is launched from is part of a project with a virtualenv."
(when (project-current)
(let ((pythonpath
(nth 0 (directory-files-recursively
(nth 2 (project-current)) "python$"))))
(when (file-exists-p pythonpath)
(setq-local python-shell-interpreter pythonpath)))))
(defun einar-radio ()
"Plays a radio stream with mpd and mpc. I stop with 'mpc stop' bound in Sway."
(interactive)
(let* ((stations #s(hash-table
size 52
test equal
data ("BBC Radio 1" "http://as-hls-ww-live.akamaized.net/pool_01505109/live/ww/bbc_radio_one/bbc_radio_one.isml/bbc_radio_one-audio%3d96000.norewind.m3u8"
"BBC Radio 2" "http://as-hls-ww-live.akamaized.net/pool_74208725/live/ww/bbc_radio_two/bbc_radio_two.isml/bbc_radio_two-audio%3d96000.norewind.m3u8"
"BBC Radio 3" "http://as-hls-ww-live.akamaized.net/pool_23461179/live/ww/bbc_radio_three/bbc_radio_three.isml/bbc_radio_three-audio%3d96000.norewind.m3u8"
"BBC Radio 4" "http://as-hls-ww-live.akamaized.net/pool_55057080/live/ww/bbc_radio_fourfm/bbc_radio_fourfm.isml/bbc_radio_fourfm-audio%3d96000.norewind.m3u8"
"BBC Radio 5" "http://as-hls-ww-live.akamaized.net/pool_89021708/live/ww/bbc_radio_five_live/bbc_radio_five_live.isml/bbc_radio_five_live-audio%3d96000.norewind.m3u8"
"BBC Worldservice" "http://as-hls-ww-live.akamaized.net/pool_87948813/live/ww/bbc_world_service/bbc_world_service.isml/bbc_world_service-audio%3d96000.norewind.m3u8"
"NRK P1 Sørlandet" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p1_dk10"
"NRK P1 Østfold" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p1_dk15"
"NRK P1 Hordaland" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p1_dk8"
"NRK P1+" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p1pluss"
"NRK P2" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p2"
"NRK P3" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p3"
"NRK P3 Urørt" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/urort"
"NRK P3X" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p3x"
"NRK P13" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/p13"
"NRK MP3" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/mp3"
"NRK Nyheter" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/nyheter"
"NRK Super" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/radio_super"
"NRK Klassisk" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/klassisk"
"NRK Jazz" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/jazz"
"NRK Folkemusikk" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/folkemusikk"
"NRK Sport" "https://lyd.nrk.no/icecast/mp3/low/s0w7hwn47m/sport"
"P4 Lyden av Norge" "https://p4.p4groupaudio.com/P04_MM"
"P5 Hits" "https://p5.p4groupaudio.com/P05_MM"
"P6 Rock" "https://p6.p4groupaudio.com/P06_MM"
"P7 Klem" "https://p7.p4groupaudio.com/P07_MM"
"P8 Pop" "https://p8.p4groupaudio.com/P08_MM"
"P9 Retro" "https://p9.p4groupaudio.com/P09_MM"
"P10 Country" "https://p10.p4groupaudio.com/P10_MM"
"P11 Bandit" "https://p11.p4groupaudio.com/P11_MM"
"NRJ Hit Music Only" "https://nrj.p4groupaudio.com/NRJ_MM"
"Radio Lyngdal" "https://live.radiolyngdal.com/rly_hq"
"Radio Norge" "https://live-bauerno.sharp-stream.com/radionorge_no_mp3"
"DR P1" "http://live-icy.gss.dr.dk/A/A03L.mp3"
"DR P2 Klassisk" "http://live-icy.gss.dr.dk/A/A04L.mp3"
"DR P3" "http://live-icy.gss.dr.dk/A/A05L.mp3"
"DR P4 København" "http://live-icy.gss.dr.dk/A/A08L.mp3"
"DR P4 Esbjerg" "http://live-icy.gss.dr.dk/A/A15L.mp3"
"DR P4 Syd" "http://live-icy.gss.dr.dk/A/A12H.mp3"
"DR P4 Bornholm" "http://live-icy.gss.dr.dk/A/A06L.mp3"
"DR P5" "http://live-icy.gss.dr.dk/A/A25H.mp3"
"DR P6 Beat" "http://live-icy.gss.dr.dk/A/A29H.mp3"
"DR P8 Jazz" "http://live-icy.gss.dr.dk/A/A22L.mp3"
"SR P1" "https://sverigesradio.se/topsy/direkt/132-mp3.pls"
"SR P2" "https://sverigesradio.se/topsy/direkt/2562-mp3.pls"
"SR P2 Språk och Musik" "https://sverigesradio.se/topsy/direkt/163-mp3.pls"
"SR P3" "https://sverigesradio.se/topsy/direkt/164-mp3.pls"
"SR P3 Din Gata" "https://sverigesradio.se/topsy/direkt/2576-mp3.pls"
"SR P4 Malmöhus" "https://sverigesradio.se/topsy/direkt/207-mp3.pls"
"SR P4 Värmland" "https://sverigesradio.se/topsy/direkt/204-mp3.pls"
"SR P4 Örebro" "https://sverigesradio.se/topsy/direkt/221-mp3.pls"
"SR P6" "https://sverigesradio.se/topsy/direkt/166-mp3.pls"
"SR Ekot sänder direkt" "https://sverigesradio.se/topsy/direkt/4540-mp3.pls")))
(url (gethash (completing-read "Velg en radiokanal: " (hash-table-keys stations)
nil t) stations)))
(shell-command "mpc clear")
(shell-command (concat "mpc add " url))
(shell-command "mpc play")))
(defun einar-play-album()
"Plays an album from my music collection with mpd through mpc."
(interactive)
(let ((album (completing-read "Velg en plate du vil spille: " (split-string (shell-command-to-string "mpc list album") "\n") nil t)))
(shell-command "mpc clear")
(shell-command (concat "mpc findadd album \"" album "\""))
(shell-command "mpc play")))
(defun einar-hjemmeside()
"Generates html-files in ~/repos/pages/ from org files in
~/Dokumenter/blog/posts/, stages, commits and pushes to codeberg. The result
can be seen at https://mostad.eu/ or https://einar.codeberg.page/."
(interactive)
(mapc 'delete-file (directory-files "/home/einar/repos/pages/" t "\\.html$"))
(delete-file "/home/einar/repos/pages/rss.xml")
(org-static-blog-publish)
(shell-command "cd /home/einar/repos/pages/ && git add .")
(setq einar-dato (format-time-string "%F"))
(setq einar-klokka (format-time-string "%H:%M"))
(shell-command (concat "cd /home/einar/repos/pages/ && git commit -m 'Newly generated html from org files on " einar-dato " at " einar-klokka ".'"))
(shell-command "cd /home/einar/repos/pages/ && git push"))
(defun einar-dired-convert-to-org ()
"Converts files pandoc can convert to org files for easy editing. Use in dired."
(interactive)
(let ((endelse (file-name-extension (dired-get-file-for-visit)))
(filnavn (dired-get-file-for-visit))
(buffernavn (concat (file-name-nondirectory (dired-get-file-for-visit)) ".org")))
(with-output-to-temp-buffer buffernavn
(princ (shell-command-to-string (concat "pandoc -f " endelse " -t org --wrap=none \"" filnavn "\""))))
(switch-to-buffer-other-window buffernavn)))
(defun einar-ispell-toggle ()
"Toggle ispell dictionary between British English and Norwegian Bokmål."
(interactive)
(if (equal ispell-dictionary "no")
(ispell-change-dictionary "british")
(ispell-change-dictionary "no")))
(defun einar-ansi-term ()
"Launch ansi-term with bash."
(interactive)
(ansi-term "bash"))
(defun einar-hardcore-mode ()
"A simple reimplementation of Magnar Sveen's hardcore mode."
(interactive)
(message "Ikke bruk piltastene! Bruk C-n, C-p, C-f og C-b i stedet!"))
Keybindings, general UI and UX, window management
I use default keybindings and add a few in my own keymap and the occasional in other keymaps. Keybindings for specific modes are bound in their use-package configuration, even if bound to einar-map. C-z is a good prefix since it works in both a TTY, a graphical terminal and GUI Emacs on every OS and since I never use iconify (GUI) or sleep.
(use-package emacs
:init (define-prefix-command 'einar-map) ; Make einar-map
(setq gc-cons-treshold (* 4 1024 1024)) ; Spend less time on GC
:bind-keymap ("C-z" . einar-map) ; Bind C-z to einar-map
:bind (:map einar-map
("C-d" . einar-toggle-photo-metadata)
("h" . einar-hjemmeside)
("i" . einar-ispell-toggle)
("m" . mu4e)
("p" . einar-play-album)
("o" . einar-dired-convert-to-org)
("r" . einar-radio)
("t" . einar-ansi-term)
("u" . einar-mount-toggle)
("w" . einar-side-window-side)
("y" . einar-systemcrafters)
:map global-map
("<up>" . einar-hardcore-mode)
("<down>" . einar-hardcore-mode)
("<left>" . einar-hardcore-mode)
("<right>" . einar-hardcore-mode)
("C-x C-d" . dired)) ; I never want to list a dir
:config (setopt user-full-name "Einar Mostad"
initial-scratch-message
(concat "🏳️🌈 Happy Hacking på " (capitalize system-name) ", " (capitalize user-login-name) "! 🏳️🌈\n\n")
load-prefer-newer t ; Use newest file
auth-sources '("~/.authinfo.gpg") ; Use encrypted file
epg-pinentry-mode 'loopback ; Pinentry in Emacs if triggered there
sentence-end-double-space nil ; Sentences end in ". " Use M-a & M-e
save-interprogram-paste-before-kill t ; Kill ring saves clipboard
mouse-yank-at-point t ; Insert at point, not at click
use-short-answers t ; Answer y or p, not yes or no
use-dialog-box nil ; No dialogues -> keyboard use
doc-view-resolution 300 ; Works on 4k screen at work
make-backup-files nil ; Turn off backup, autosave is enough
vc-follow-symlinks t ; Follow symlinks to vc'ed files
isearch-lazy-count t ; Show which result and total
recenter-positions '(top middle bottom) ; Top first, then middle
scroll-conservatively 100 ; Don't center point when scrolling
window-combination-resize t ; Equal size windows in combinations
help-window-select t ; Automatically select help buffers
minibuffer-visible-completions t ; M-n / M-p in m-b to select
completions-detailed t
completion-auto-help 'always
read-buffer-completion-ignore-case t
read-file-name-completion-ignore-case t
completion-auto-wrap t ; wrap from last to first completion
completion-auto-help 'visible
completion-auto-select t
completion-show-help nil
completions-header-format nil
completions-format 'one-column ; One vertical column
completions-max-height 10 ; with 10 lines height
; completion-eager-update t ; Emacs 31: continue to type in minibuffer to narrow candidates in completion buffer
completion-styles '(basic flex) ; Basic for Tramp, fuzzy-find
history-length 50) ; Fewer than default to reduce latency
(setq completion-ignore-case t ; Ignore case in minibuffer completions
completion-detailed t
native-comp-async-report-warnings-errors 'silent ; Log, don't annoy
reb-re-syntax 'string ; No double escape in regexps builder
custom-file (locate-user-emacs-file "custom-vars.el"))
(load custom-file 'noerror 'nomessage) ; ^ no customize in config.el, load it
(savehist-mode 1) ; Rember minibuffer history
(scroll-bar-mode -1) ; Percent in modeline shows position
(tool-bar-mode -1) ; No, I don't want to mouse around
(menu-bar-mode -1) ; Discoverability is good without it
(display-battery-mode 1) ; Status and percent in modeline
(line-number-mode 1) ; Line and column numbers in modeline
(column-number-mode 1) ; for debugging - not in buffers
(global-visual-line-mode 1) ; Wrap long lines
(global-goto-address-mode 1) ; URLs are links, follow with C-c RET
(save-place-mode 1) ; Remember cursor position
(global-auto-revert-mode 1) ; Change buffers when files change
(repeat-mode 1) ; Repeat key chord with last key
(winner-mode 1) ; Remember windows, use C-c <left>
(einar-side-window-side) ; Put side-windows righ / bottom
(add-to-list 'display-buffer-alist ; Hide async-shell-command window
(cons "\\*Async Shell Command\\*.*" (cons #'display-buffer-no-window nil)))
(load-theme 'modus-vivendi-tinted)
(setopt modus-themes-common-palette-overrides
'((border-mode-line-active unspecified) ; Turn off border on modeline
(border-mode-line-inactive unspecified)
(fringe bg-main))) ; Fringe in same colour as background
(set-face-attribute 'default nil :font "Hack" :height 120)
(set-face-attribute 'fixed-pitch nil :font "Hack" :height 1.0)
(set-face-attribute 'variable-pitch nil :font "Liberation Sans" :height 1.0)
(prefer-coding-system 'utf-8-unix) ; Try to get rid of non-UTF-8 files...
(set-default-coding-systems 'utf-8-unix)
(set-language-environment 'utf-8)
(setq-default coding-system-for-write 'utf-8-unix)
(setq-default buffer-file-coding-system 'utf-8-unix))
Package repositories
(use-package package
:ensure nil
:config ; I need MELPA for batppuccin, nov, kv, elfeed and ox-pandoc
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(setq package-archive-priorities
'(("gnu" . 3)
("nongnu" . 2)
("melpa" . 1))))
Spelling (flyspell, ispell)
I use C-z i to toggle between British English and Norwegian Bokmål spelling dictionaries.
(use-package flyspell
:hook ((prog-mode . flyspell-prog-mode) ; For comments and strings
(text-mode . flyspell-mode))
:init
(setq ispell-program-name "aspell")
(setq ispell-dictionary "no"))
Dictionary lookup (not spell checking)
(use-package dictionary
:config (setopt dictionary-server "dict.org"))
Bookmarks
(use-package bookmark
:ensure nil
:config
(setopt bookmark-fringe-mark nil ; No marking of bookmarked lines in fringe
bookmark-save-flag 1)) ; Save after every edit
Diff without font lock on green/red background
The contrast might be too low when the background is green or red to also font lock.
(use-package diff
:ensure nil
:config
(setopt diff-font-lock-syntax nil))
Keyboard shortcut exploration (which-key)
(use-package which-key
:ensure t
:init (which-key-mode)
:config
(setq which-key-idle-delay 0.1))
Shells
I use Eshell a lot. When I occasionally need a terminal emulator, not just a shell, I launch ansi-term with C-z t. In the past, I used to use Shell mode with Bash on GNU/Linux and PowerShell on Windows. Eshell has replaced both now and is a much more pleasant experience.
Eshell
I like the cursor to move with the output like in normal shells. I set up a few useful aliases. View-mode is a good replacement for less.
(use-package eshell
:config
(setopt eshell-banner-message (concat "🏳️🌈 Velkommen til Eshell på " (capitalize system-name) ", " (capitalize user-login-name) "! 🏳️🌈\n\n")
eshell-history-append t
eshell-history-size 513
eshell-ls-initial-args '("-A"))
(setq eshell-where-to-jump 'end
eshell-smart-space-goes-to-end t)
(use-package em-alias
:config
(eshell/alias "f" "find-file 1ドル")
(eshell/alias "d" "dired 1ドル")
(eshell/alias "less" "view-file 1ドル")
(eshell/alias "clear" "clear-scrollback"))
:bind (:map einar-map
("C-e" . eshell)))
Shell mode
I turn on ANSI colours. I set shell mode to pretend it is xterm with 256 colour support.
(use-package shell
:config
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)
(add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
(add-hook 'comint-output-filter-functions 'comint-osc-process-output)
(setq explicit-shell-file-name "bash")
:bind (:map einar-map
("s" . shell)))
ANSI-term
I use ANSI-term when I occasionally need a terminal, for example for pulsemixer. I turn on ANSI colours. I set term to pretend it is xterm with 256 colour support.
(use-package term
:config
(add-hook 'term-mode-hook 'ansi-color-for-comint-mode-on)
(add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
(add-hook 'comint-output-filter-functions 'comint-osc-process-output)
(setq system-uses-terminfo t)
(setopt comint-terminfo-terminal "xterm-256color"))
Org Mode, presentations and blog
Org configuration
Adds programming languages Org Mode can evaluate, sets indentation in code blocks to 0 (important for python), makes sure the indentation I write is preserved, lets tab act like in the major mode for the programming language, makes fontlocking work like in the major mode for the language, org export without table of contents, postamble, smart quoutes, section numbers or superscript or subscript (if t, snake_case becomes snake_subscript), makes margins 1.5 cms always and wastes less space. (I don't write scientific papers.), lets you write code directly into code blocks instead of opening another window which is useful for changing code blocks interactively while presenting them in class. Then comes org-agenda configuration where I set M-RET to not split headings or insert a new headline over text already written underneath one, set it to log events by time, put it in a drawer, include Emacs diary and calendar in org-agenda, turn off warnings of upcoming deadlines for TODOs that are scheduled as well, set org-agenda to use files stored in ~/repos/org/, and specify keywords for TODOs.
(use-package org
:defer 0
:config
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(eshell . t)
(shell . t)
(C . t)
(makefile . t)
(python . t)
(scheme . t)
(sql . t)
(js . t)
(css . t)))
(setq org-edit-src-content-indentaton 0)
(setq org-src-preserve-indentation t)
(setq org-src-tab-acts-natively t)
(setq org-src-fontify-natively t)
(setq org-export-with-toc nil)
(setq org-export-with-section-numbers nil)
(setq org-export-with-smart-quotes nil)
(setq org-export-with-sub-superscripts nil)
(setq org-html-postamble nil)
(setq org-latex-packages-alist '(("cm" "fullpage") ("avoid-all" "widows-and-orphans") ("" "svg")))
(setq org-src-window-setup 'current-window)
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-insert-heading-respect-content nil)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-agenda-include-diary t)
(setq org-agenda-skip-deadline-prewarning-if-scheduled t)
(setq org-directory "~/repos/org/")
(setq org-agenda-files (list org-directory))
(setq org-todo-keywords
'((sequence "TODO(t)" "WAIT(w!)" "|" "CANCEL(c!)" "DONE(d!)")))
:bind (:map einar-map
("c" . org-insert-structure-template)
("d" . org-display-inline-images)
("l" . org-toggle-link-display)))
Ox-pandoc for docx and odt export
(use-package ox-pandoc
:if (not (equal user-login-name "furios"))
:ensure t
:after org)
Presentations (inter-present-mode)
A simple interactive presentation mode for org mode. I use it at work every day. See the README.
(use-package inter-present-mode
:vc (:url "https://codeberg.org/einar/inter-present-mode.git"
:rev :newest)
:commands inter-present-mode
:bind (:map einar-map
("C-s" . (lambda () (interactive) (inter-present-mode 'toggle))))
:config (setopt inter-present-mode-keymap-prefix "C-z"
inter-present-mode-face-height 280
inter-present-mode-start-heading nil
inter-present-mode-highlight-line t))
Blog (org-static-blog)
I use org-static-blog for my website. A header is inserted into the <head> section of every page. A preamble is inserted at the beginning of the <body> of every page which is ideal for a top menu. A postamble is inserted at the end of the <body> of every page which is ideal for a footer. The index-front-matter HTML is inserted into the index page between the preamble and the blog posts.
(use-package org-static-blog
:ensure t
:commands org-static-blog-mode
:init
;; Undeclared dependency that it actually needed
(use-package htmlize
:ensure t)
(setq org-static-blog-publish-title "Einar's blog")
(setq org-static-blog-publish-url "https://einar.codeberg.page/")
(setq org-static-blog-publish-directory "~/repos/pages/")
(setq org-static-blog-posts-directory "~/Dokumenter/blog/posts/")
(setq org-static-blog-drafts-directory "~/Dokumenter/blog/drafts/")
(setq org-static-blog-enable-tags t)
(setq org-static-blog-enable-tag-rss t)
(setq org-export-with-toc nil)
(setq org-export-with-section-numbers nil)
(setq org-static-blog-page-header
"\n\n<meta name=\"author\" content=\"Einar Mostad\">
<meta name=\"referrer\" content=\"no-referrer\">
<link href= \"style.css\" rel=\"stylesheet\" type=\"text/css\"</>
<link rel=\"icon\" href=\"favicon.ico\">")
(setq org-static-blog-page-preamble
"<div class=\"header\">
<a class=\"sitelinks\" href=\"index.html\">🏠 Home</a>
<a class=\"sitelinks\" href=\"about.html\">About</a>
<a class=\"sitelinks\" href=\"archive.html\">Archive</a>
<a class=\"sitelinks\" href=\"tag-links.html\">Tags</a>
<a class=\"sitelinks\" href=\"rss.xml\">RSS</a>
</div>")
(setq org-static-blog-page-postamble
"© Einar Mostad 2010 - 2026. Content is licensed under the terms of <a rel=\"license\" href=\"https://creativecommons.org/licenses/by-sa/4.0/\">CC BY SA</a> except code which is <a rel=\"license\" href=\"https://www.gnu.org/licenses/gpl-3.0.en.html\">GNU GPL v3</a> or later.<br>Made with <a href=\"https://www.gnu.org/software/emacs/\">GNU Emacs</a>, <a href=\"https://github.com/bastibe/org-static-blog\">Org-Static-Blog</a>, and <a href=\"https://codeberg.page/\">Codeberg Pages</a> on <a href=\"https://www.gnu.org/gnu/linux-and-gnu.html\">GNU/Linux</a>.")
(setq org-static-blog-index-front-matter "<h1>Newest posts:</h1>"))
Templates
Tempo is a built-in template package without documentation. There is an info manual on David Kågedal's homepage and some examples of its use. I set tempo-interactive to nil to go to marks inline with C-z f or C-z b instead of completing the template in the minibuffer. To insert the template, I write a tag and then press C-z a.
(use-package tempo
:ensure nil
:commands tempo-complete-tag
:config
(setq tempo-interactive 'nil)
(tempo-define-template "definition"
'("(tempo-define-template \"" p "\" '(\"" r "\") \"" p "\" \"" p "\")")
"tempo" "A tempo template to define tempo templates.")
(tempo-define-template "python-def"
'("def " p "(" p ": " p ") -> " p ":\n"
" \"\"\"\n " p "\n \"\"\"\n " r)
"pydef" "A template for a python function definition.")
(tempo-define-template "emacs-lisp-defun"
'("(defun " p " (" p ")" n> "\"" p "\"" n> r ")")
"eldefun" "A template for an Emacs-Lisp function definition.")
:bind (:map einar-map
("a" . tempo-complete-tag)
("b" . tempo-backward-mark)
("f" . tempo-forward-mark)))
Programming
Tab-key for indentation and completion
It might be worth trying (setopt global-completion-preview-mode t) as well to automatically show the completion preview based on the text at point.
(setopt tab-always-indent 'complete
tab-first-completion 'nil)
Indent with spaces (but press tab to get them)
(setopt indent-tabs-mode nil)
80 column width (auto-fill-mode)
Sets 80 columns width and turns auto-fill-mode on in all programming modes. (Even if I would prefer 100, 80 is standard.)
(setq-default fill-column 79)
(add-hook 'prog-mode-hook 'auto-fill-mode)
Dumb-jump to jump to definitions
Dumb-jump uses ripgrep or another grep-like search to find definitions instead of using tags or LSP. The package comes from Melpa. The last line sets it to use completion-read instead of a separate buffer with candidates popping up.
(use-package dumb-jump
:ensure t
:config
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
(setq dumb-jump-force-searcher 'rg)
(setq xref-show-definitions-function #'xref-show-definitions-completing-read))
Colour codes (Rainbow mode)
When you write a colour code like #3641FF, the background is set to the colour if you are in a programming mode. Very useful for CSS.
(use-package rainbow-mode
:ensure t
:hook (prog-mode))
Highlight changes in the margin in git repos
(use-package diff-hl
:ensure t
:hook (dired-mode . diff-hl-dired-mode)
:config
(global-diff-hl-mode 1)
(diff-hl-flydiff-mode 1)
(unless (display-graphic-p)
(diff-hl-margin-mode 1)))
Web mode for HTML, CSS, PHP, .aspx etc
Makes the experience better when one language is embedded in another like when editing Jinja2 templates for Flask.
(use-package web-mode
:ensure t
:mode "\\.html?\\'"
:mode "\\.css\\'"
:mode "\\.phtml\\'"
:mode "\\.tpl\\.php\\'"
:mode "\\.[agj]sp\\'"
:mode "\\.as[cp]x\\'"
:mode "\\.erb\\'"
:mode "\\.mustache\\'"
:mode "\\.djhtml\\'"
:config
(setq web-mode-markup-indent-offser 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2))
JavaScript indentation at 2 spaces
(setq js-indent-level 2)
Python
I think the Django style of docstrings are more aesthetic than pep-257. I add einar-python-virtualenv to python-mode-hook which sets the python-shell-interpreter locally for a file to its project's virtual environment if there is one. This means every python shell I open from a file knows the packages installed in the virtual environment of the project that file is part of if there is one.
(use-package python
:config
(setq python-fill-docstring-style 'django)
(setenv "PYTHON_BASIC_REPL" "1")
(add-hook 'python-mode-hook 'einar-python-virtualenv))
(Bash) Shell
Less indentation means more code within 80 columns.
(setq-default sh-indentation 2)
Clojure
(use-package cider
:ensure t
:config
(setq cider-show-error-buffer t
cider-auto-select-error-buffer t
cider-repl-history-file "~/.emacs.d/cider-history"
cider-repl-pop-to-buffer-on-connect t
cider-repl-wrap-history t)
(add-to-list 'auto-mode-alist '("\\.boot$" . clojure-mode))
(add-to-list 'auto-mode-alist '("\\.cljs.*$" . clojure-mode))
(add-to-list 'auto-mode-alist '("lein-env" . enh-ruby-mode)))
Document viewer (Reader mode)
Reader mode is a document viewer that is faster than doc-view-mode and can handle more formats (PDF, EPUB, MOBI, FB2, XPS/OpenXPS, CBZ, ODT, ODS, ODP, ODG, DOCX, PPTX and XLSX). It will replace doc-view-mode and nov-mode for me. It uses libmu >1.26.
On Guix, I tried both the user-package-configuration in the README.org and adding the divya-lambda channel and installing from there and nonething worked. Debian 13 and RPiOS Light has too old libmu and misses a file it needs because of how it is packaged. The newest libmu needs building from source to work. I'll wait for it to be packaged in Guix or ELPA.
Web Browser (EWW)
Set title of EWW buffer to title of webpage (better if more than one is open). Set max width of text to window size. Set max size of images to 80% of window size. Set to keep history even when continuing from a previous URL in history. Keep 200 sites in history (URL and rendering of it). Set to standard Downloads folder in the Norwegian Bokmål locale.
(defun einar-browse-url-mpv (url &rest _args)
"Open URL in mpv."
(start-process "mpv" nil "mpv" url))
(defun einar-browse-url-pdf (url &rest _args)
"Fetch remote PDF and open in doc-view-mode in Emacs."
(let ((tmp (make-temp-file "emacs-pdf-" nil ".pdf")))
(url-copy-file url tmp t)
(find-file-other-window tmp)
(doc-view-mode)))
(defun einar-download-image-at-point ()
"Download image at point to `eww-download-directory'."
(interactive)
(let ((url (or (get-text-property (point) 'image-url)
(get-text-property (point) 'shr-url))))
(if (not url)
(message "No image at point")
(let* ((filename (file-name-nondirectory (url-filename (url-generic-parse-url url))))
(dest (expand-file-name filename eww-download-directory)))
(url-copy-file url dest t)))))
(use-package eww
:config
(setopt browse-url-browser-function 'eww-browse-url
browse-url-secondary-browser-function 'browse-url-generic
browse-url-generic-program "librewolf"
eww-auto-rename-buffer 'title
eww-header-line-format ""
shr-max-width 'nil
shr-width 'nil
shr-max-image-proportion 0.8
shr-use-colors nil
shr-use-fonts nil
eww-before-browse-history-function 'eww-delete-future-history
eww-history-limit 200
browse-url-handlers
'(("\\(youtube\\.com\\|youtu\\.be\\|vimeo\\.com\\|twitch\\.tv\\)" . einar-browse-url-mpv)
("\\.mp3$" . einar-browse-url-mpv)
("\\.mp4$" . einar-browse-url-mpv)
("\\.webp$" . einar-browse-url-mpv)
("\\.webm$" . einar-browse-url-mpv)
("\\.m4v$" . einar-browse-url-mpv)
("\\.mkv$" . einar-browse-url-mpv)
("\\.pdf$" . einar-browse-url-pdf)
;; Change function to use end of url as Suffix for make-temp-file for these:
;; ("\\.docx$" . einar-browse-url-pdf)
;; ("\\.odt$" . einar-browse-url-pdf)
("." . eww-browse-url)))
(setq eww-download-directory "~/Nedlastinger")
(define-key eww-mode-map (kbd "D") #'einar-download-image-at-point))
Calendar, Diary and World Clock
Calendar
Sets longitude, latitude and location. Makes M-x sunrise-sunset and S from the calendar work. Makes the calendar show days from the diary, holidays and other commemorated days. Show holidays and other commemorated days in the diary buffer. Shows reminders in a window for 60 seconds 10 minutes before an event. Show in the modeline the time to the event and make a sound. Show days without content in the diary file when pressing d in calendar. This is useful for days that are holidays and other commemorated days. Sorts events for fancy display of diary. Does not show diary whenever calendar is displayed for 7 days ahead in time. Sets the calendar to not open in a separate frame together with the diary. I put my diary file in ~/Dokumenter/.
(use-package calendar
:ensure nil
:commands (calendar)
:init
(setq calendar-latitude 58.1)
(setq calendar-longitude 7.1)
(setq calendar-location-name "Lyngdal, Norge")
(setq calendar-mark-diary-entries-flag t)
(setq calendar-mark-holidays-flag t)
(setq diary-show-holidays-flag t)
(setq appt-display-format 'window)
(setq appt-display-duration 60)
(setq appt-message-warning-time 10)
(setq appt-display-mode-line t)
(setq appt-audible t)
(setq apt-display-diary nil)
(setq diary-list-include-blanks t)
(setq calendar-view-diary-initially-flag nil)
(setq diary-number-of-entries 7)
(setq calendar-setup 'nil)
(appt-activate 1)
(add-hook 'diary-list-entries-hook 'diary-sort-entries t)
(setq diary-file "/home/einar/Dokumenter/kalender"))
Clock in the modeline and world clock
Sets the clock to 24 hours, shows it in the modeline, turns off load-average in the modeline, show cities ordered North to South (closer first) and with Norwegian names for the world clock (M-x world-clock).
(use-package time
:hook (after-init . display-time-mode)
:custom
(display-time-24hr-format t)
(display-time-day-and-date t)
(display-time-format "%a %0e.%0m. U%V %R")
(world-clock-time-format "%R %Z %a %0e.%0m.")
(display-time-default-load-average nil)
(zoneinfo-style-world-list
'(("America/Los_Angeles" "Vancouver, Seattle, Los Angeles")
("America/New_York" "Montréal, New York, Panama, Lima")
("Europe/London" "Reykjavík, London, Lisboa")
("Europe/Oslo" "Oslo, Stockholm, København, Berlin, Paris, Lagos")
("Europe/Helsinki" "Helsinki, Tallin, Riga, Kyiv, Sofia, Aten")
("Asia/Istanbul" "Istanbul, Baghdad, Johannesburg")
("Asia/Hong_Kong" "Beijing, Taipei, Hong Kong, Singapore")
("Asia/Tokyo" "Seul, Tokyo")
("Australia/Melbourne" "Melbourne, Sydney")
("Pacific/Auckland" "Wellington"))))
Localisation of Calendar to Norwegian Bokmål
Localises the calendar with Norwegian Bokmål terms, time and date formats, red calendar days, flag days, name days, often commemorated days and start and stop of summer time with the help of my package Calloc.
(use-package calloc
:vc (:url "https://codeberg.org/einar/calloc.git"
:rev :newest)
:after calendar
:config
(setopt calloc-locale "nb_NO"
calloc-enable-red-days t
calloc-enable-flag-days nil
calloc-enable-extra-flag-days ""
calloc-enable-commemorated-days t
calloc-enable-summertime-days t))
File Manager (dired, image-dired, dired-async)
Dired with my einar-mount-toggle function does everything I need from a file manager, so I no longer use pcmanfm.
Dired
Makes dired show all files in a human readable list (with k, M, G and T). Change dired buffer if files have changed on disk. Git rename if file name changes in dired in a repo. Makes the suggested command xdg-open for every file type you run with ! or &. Kills a dired buffer if you enter a new directory. (To stay within one window.) Uses the next window in the same frame as default target for dired. Sets copy and deletion of folders to recursive.
(use-package dired
:ensure nil
:commands dired
:bind (:map dired-mode-map
("b" . dired-up-directory))
:config
(setopt dired-listing-switches "-ahl"
global-auto-revert-non-file-buffers t
dired-auto-revert-buffer t
dired-vc-rename-file t
dired-guess-shell-alist-user
'((".mkv" "mpv")
(".m4v" "mpv")
(".mp4" "mpv")
(".mp3" "mpv")
(".webm" "mpv")
(".m4a" "mpv")
(".iso" "mpv")
(".jpg" "gimp")
(".jpeg" "gimp")
(".gif" "gimp")
(".png" "gimp"))
dired-kill-when-opening-new-dired-buffer t
dired-dwim-target t
dired-recursive-copies 'always
dired-recursive-deletes 'always
dired-create-destination-dirs 'ask)
(add-hook 'dired-mode-hook (lambda () (dired-hide-details-mode 1))))
Dired Async Mode to avoid locking Emacs
Installs and turns on async and dired-async for non-locking copying of files. It improves the "Emacs for everything" experience a lot.
(use-package async
:ensure t
:after dired)
(use-package dired-async
:after async
:hook (dired-mode . dired-async-mode))
RSS reader (Elfeed)
Sets elfeed to show unread feed entries by default. Configure feeds in three categories and set feeds based on user-name (= device). Adds keybinding for my custom functions.
(use-package elfeed
:ensure t
:commands elfeed
:config
(setq-default elfeed-search-filter "+unread ")
(let ((podcasts '(("https://feeds.soundcloud.com/users/soundcloud:users:627190089/sounds.rss" Clojure Stream podcast Jacek)
("https://feeds.kodsnack.se/" Kodsnack Svenska)
("https://latenightlinux.com/feed/mp3" Late Night Linux)
("https://api.dr.dk/podcasts/v1/feeds/stjerner-og-striber-podcast" DR Stjerner og striber)
("https://fsfe.org/news/podcast.en.rss" Software Freedom Podcast)
("https://feeds.twit.tv/ww.xml" Windows Weekly)))
(articles '(("https://www.debian.org/News/news" Debian Project News GNU Linux)
("https://planet.guix.gnu.org/atom.xml" Planet Guix)
("https://librecmc.org/rss_releases.xml" libreCMC GNU Linux-libre)
("http://github.com/swaywm/sway/releases.atom" Sway)
("https://blog.python.org/feeds/posts/default?alt=rss" Python News)
("https://www.darktable.org/news/index.xml" Darktable Foto)
("https://mntre.com/reform_md.atom" MNT Reform Maskinvare)
("https://furilabs.com/feed.xml" Furilabs Maskinvare)
("https://spritely.institute/feed.xml" Spritely Institute Guile Scheme)
("https://fsfe.org/news/news.en.rss" FSFE News EU Europe)
("https://www.fsf.org/static/fsforg/rss/news.xml" FSF News)
("https://blog.codeberg.org/feeds/all.atom.xml" Codeberg Blog)
;("https://planet.emacslife.com/atom.xml" Planet Emacs GNU)
("https://sachachua.com/blog/category/emacs-news/feed/" Sacha Chua Emacs Weekly News)
("https://fosdem.org/2027/rss.xml" FOSDEM)
("https://blog.josefsson.org/feed/" Simon Josefsson GNU Guix Debian)
("https://www.more-magic.net/feed.atom" Peter Bex Scheme)
("https://jxself.org/rss20.xml" Jason Self FSF GNU)
("https://www.freakingpenguin.com/feed.xml" Richard Sent Emacs Guix)
("https://morrick.me/feed" Riccardo Mori Apple Macintosh)
("https://zck.org/feed.rss" Zack Emacs Cyborg)
("https://datagubbe.se/atom.xml" Datagubbe GNU Linux Emacs)
("https://magnars.com/atom.xml" Magnar Sveen Clojure Emacs)
("https://cjohansen.no/atom.xml" Christian Johansen Clojure Emacs)
("https://parenteser.mattilsynet.io/atom.xml" Parenteser Mattilsynet Clojure norsk)
("https://cestlaz.zamansky.net/rss.xml" Mike Zamansky Undervisning Emacs)
("https://erikjohannes.no/index.xml" Erik Johannes Husom LLM)))
(videos '(("https://archive.org/services/collection-rss.php?collection=protesilaos-videos" Protesilaos Emacs Video Archive.org)
("https://framatube.org/feeds/videos.xml?videoChannelId=333" FSF video LibrePlanet)
("https://toobnix.org/feeds/videos.xml?videoChannelId=154" Emacsconf video))))
(if (equal user-login-name "furios")
(setq elfeed-feeds podcasts)
(setq elfeed-feeds (append articles podcasts videos))))
:bind (:map einar-map
("e" . elfeed)
:map elfeed-show-mode-map
("B" . elfeed-show-eww-open)
("P" . elfeed-show-mpv-enclosure)
("K" . elfeed-show-dl-enclosure)
("m" . elfeed-show-mpv-open)
("M" . elfeed-show-ytdlp-open)
:map elfeed-search-mode-map
("B" . elfeed-search-eww-open)
("m" . elfeed-search-mpv-open)
("M" . elfeed-search-ytdlp-open)))
;; Extra functions
(defun elfeed-show-eww-open (&optional use-generic-p)
"open with eww"
(interactive "P")
(let ((browse-url-browser-function #'eww-browse-url))
(elfeed-show-visit use-generic-p)))
(defun elfeed-search-eww-open (&optional use-generic-p)
"open with eww"
(interactive "P")
(let ((browse-url-browser-function #'eww-browse-url))
(elfeed-search-browse-url use-generic-p)))
(defun elfeed-show-mpv-enclosure (enclosure-index)
(interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry)))
(let ((url (car
(elt
(elfeed-entry-enclosures elfeed-show-entry)
(- enclosure-index 1)))))
(start-process "mpv" "*mpv*" "mpv" url)))
(defun elfeed-show-dl-enclosure (enclosure-index)
(interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry)))
(let ((url (car
(elt
(elfeed-entry-enclosures elfeed-show-entry)
(- enclosure-index 1)))))
(shell-command (concat "cd ~/Nedlastinger && " "yt-dlp " url " &"))))
(defun browse-url-mpv (url &optional single)
(start-process "mpv" "*mpv*" "mpv" url))
(defun elfeed-show-mpv-open (&optional use-generic-p)
"open with mpv"
(interactive "P")
(let ((browse-url-browser-function #'browse-url-mpv))
(elfeed-show-visit use-generic-p)))
(defun elfeed-search-mpv-open (&optional use-generic-p)
"open with mpv"
(interactive "P")
(let ((browse-url-browser-function #'browse-url-mpv))
(elfeed-search-browse-url use-generic-p)))
(defun browse-url-ytdlp (url &optional single)
(shell-command (concat "cd ~/Nedlastinger &&" "yt-dlp " url " &")))
(defun elfeed-show-ytdlp-open (&optional use-generic-p)
"open with ytdlp"
(interactive "P")
(let ((browse-url-browser-function #'browse-url-ytdlp))
(elfeed-show-visit use-generic-p)))
(defun elfeed-search-ytdlp-open (&optional use-generic-p)
"open with ytdlp"
(interactive "P")
(let ((browse-url-browser-function #'browse-url-ytdlp))
(elfeed-search-browse-url use-generic-p)))
Music (mpc mode)
Mpc mode is a built-in Rhythmbox-like music player that uses mpd (music player deamon) on the back end. I set it to display artist and albums (but usually keep artist at All since I like browsing my music collection by album). I set some keybindings so I can add tracks to the playlist with a and delete the playlist with d.
(use-package mpc
:ensure nil
:commands mpc
:config
(setq mpc-browser-tags '(Artist Album))
(keymap-set mpc-mode-map "a" 'mpc-playlist-add)
(keymap-set mpc-mode-map "d" 'mpc-playlist-delete))
Email (mu4e)
I don't need this on most machines, so I keep the settings in a separate file and load it only if it exists.
(if (file-readable-p "/home/einar/.emacs.d/server.el")
(load-file "/home/einar/.emacs.d/server.el"))
Ebooks (Nov mode)
Installs nov for reading epubs with Emacs, make epubs open in nov mode and makes nov use /usr/bin/unzip. Remember to install unzip. Nov depends on kv even if it doesn't install it.
(use-package nov
:ensure t
:commands nov-mode
:init
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
(setq nov-unzip-program "unzip")
(setq nov-text-width nil))
Process editor
Emacs comes with a process editor that resembles htop. It has nice keybindings alla dired.
(use-package proced
:ensure nil
:defer t
:custom
(proced-enable-color-flag t)
(proced-tree-flag t)
(proced-format-alist
'((short user pid tree pcpu pmem start time comm)
(medium user pid tree pcpu pmem vsize rss ttname state start time
(args comm))
(long user euid group pid tree pri nice pcpu pmem vsize rss ttname state start time
(args comm))
(verbose user euid group egid pid ppid tree pgrp sess pri nice pcpu pmem state thcount vsize rss ttname tpgid minflt majflt cminflt cmajflt start time utime stime ctime cutime cstime etime
(args comm)))))
Copyright and license
This work is shared under the terms of the GNU General Public License version 3 or later (at your choice). See the License file for more information.
Copyright 2022-2026, Einar Mostad