2755 lines
99 KiB
Org Mode
2755 lines
99 KiB
Org Mode
#+TITLE: Emacs Configuration file
|
|
#+AUTHOR: Marcel Kapfer
|
|
#+EMAIL: opensource(at)mmk2410(dot)org
|
|
# Source: https://gitlab.com/mmk2410/dot-emacs
|
|
# License: This file is licensed under the GPL v3
|
|
#+STARTUP: indent overview
|
|
|
|
*Welcome!* You found my (Marcel Kapfer aka mmk2410) personal Emacs configuration file. Please feel free to read through it, use it or get inspiration! If you would (for some very strange reason) like to read it online: I [[https://mmk2410.org/2022/01/30/publishing-my-emacs-configuration/][published]] it to [[https://config.mmk2410.org][config.mmk2410.org]]
|
|
|
|
This file (=config.org=) is written in [[https://orgmode.org/][org-mode]] and converted as Emacs Lisp to =config.el= on startup. This is achieved on org-babel and tangle (see the =init.el= file).
|
|
|
|
There are many people that I should thank for giving me the help needed to write this config. Two of them need to be mentioned here since many parts are either copied or heavily inspired by their configurations:
|
|
|
|
- [[https://karl-voit.at/][Karl Voit]] especially for his [[https://karl-voit.at/2021/06/01/Thunderbird-to-Evolution][Evolution email workflow]] and this [[https://karl-voit.at/2017/06/03/emacs-org/][setup approach]] ([[https://github.com/novoid/dot-emacs/blob/master/config.org][his Emacs config]])
|
|
- [[https://daviwil.com/][David Wilson]] and his [[https://www.youtube.com/c/SystemCrafters][System Crafters]] YouTube channel for his [[https://www.youtube.com/c/SystemCrafters/videos][Emacs videos]] ([[https://config.daviwil.com/emacs][his Emacs config]])
|
|
|
|
/Note: Elisp blocks which are marked as =:tangle no= are not evaluated./
|
|
|
|
* A few words on setup
|
|
|
|
Writing the initial "stable" version of this configuration took nearly two years. I used it constantly during this time after I declared Emacs bankruptcy with [[https://git.mmk2410.org/mmk2410/dotfiles/src/commit/f6d220554ab0946a1a4efb7e3400194139a99550/emacs/emacs.el][my old configuration]]. During this time many things happened and changed Back in March 2019 when I started I never imaged using this configuration on a native Microsoft® Windows® installation of Emacs. But in 2021 when I started using some creative tools that are only available on Windows® I first did and only later switched completely to the Windows Subsystem for Linux. Therefore /some/ parts are compatible with a native installation of Emacs on Windows®.
|
|
|
|
For Microsoft(R) Windows(R) users it is highly adviced to set the =HOME= environment variable to the users home directory. This can be done by opening the file explorer =WIN+e=, right clicking on "This PC" and choosing "Properties". In the opening window it is possible to open "Advanced system settings" in the sidebar. In the then appearing dialg ther is a button labeled "Environment Variables...". Using the "New..." button in the "User variables" section of the "Environment Variables" dialog it is possible to add a new one. Enter as name =HOME= and use the "Browse Directory.." button for choosing your user home directory. Accept all the changes by clicking on "OK" until there are no more open dialogs left.
|
|
|
|
* General settings
|
|
|
|
Settings for Emacs in general as well as for my personal setup.
|
|
|
|
** macOS Adjustments
|
|
|
|
First define a variable for making MacOS specific adjustments a bit easier.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq on-macos (eq system-type 'darwin))
|
|
#+end_src
|
|
|
|
For using the Macbook keyboard effectively with Emacs I made some adjustments based on some suggestions found on the internet.
|
|
|
|
#+begin_src emacs-lisp
|
|
(when on-macos
|
|
(setq mac-option-modifier 'alt)
|
|
(setq mac-right-option-modifier 'none)
|
|
(setq mac-command-modifier 'meta)
|
|
(global-set-key [kp-delete] 'delete-char))
|
|
#+end_src
|
|
|
|
** System Detection
|
|
|
|
My work machine is called “knuth” and there is some configuration which I only want there.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/work ()
|
|
"Return t if machine is my work machine"
|
|
(string-equal (system-name) "knuth"))
|
|
#+end_src
|
|
|
|
** Personal Information
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq user-full-name "Marcel Kapfer"
|
|
user-mail-address "opensource@mmk2410.org")
|
|
#+end_src
|
|
|
|
** Date & Time
|
|
|
|
Set the first day of the week to Mondays.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq calendar-week-start-day 1)
|
|
#+end_src
|
|
|
|
Automatically create and save UIDs when exporting an iCalendar using Org.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-icalendar-store-UID t)
|
|
#+end_src
|
|
|
|
Set timezone for Org iCalendar export.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-icalendar-timezone "Europe/Berlin")
|
|
#+end_src
|
|
|
|
Use 24h time display including day and date.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq display-time-24hr-format t
|
|
display-time-day-and-date t)
|
|
#+end_src
|
|
|
|
Use a sane date style.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq calendar-date-style 'european)
|
|
#+end_src
|
|
|
|
** Turn off backup and lock files
|
|
|
|
Lock files don't play nice with npm.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default backup-inhibited t)
|
|
(setq create-lockfiles nil)
|
|
#+end_src
|
|
|
|
** Performance tweaks
|
|
|
|
As recommended by [[https://emacs-lsp.github.io/lsp-mode/page/performance/][lsp-mode]],
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq gc-cons-threshold (* 100 1024 1024)
|
|
read-process-output-max (* 1024 1024))
|
|
#+end_src
|
|
|
|
** Disable adding double spaces after periods
|
|
|
|
Source: [[http://www.reddit.com/r/emacs/comments/2l5gtz/real_sentence_in_emacs/][Real sentence in Emacs : emacs]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default sentence-end-double-space nil)
|
|
#+end_src
|
|
|
|
** Enable truncate lines mode
|
|
|
|
Sources:
|
|
- https://www.emacswiki.org/emacs/TruncateLines
|
|
- [[http://stackoverflow.com/questions/7577614/emacs-truncate-lines-in-all-buffers][elisp - Emacs truncate lines in all buffers - Stack Overflow]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default truncate-lines t)
|
|
(setq-default global-visual-line-mode t)
|
|
(add-hook 'org-mode-hook 'visual-line-mode)
|
|
(add-hook 'prog-mode-hook 'visual-line-mode)
|
|
#+end_src
|
|
|
|
** Delete trailing white-space
|
|
|
|
Source: [[https://www.emacswiki.org/emacs/DeletingWhitespace][EmacsWiki: Deleting Whitespace]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'before-save-hook 'delete-trailing-whitespace)
|
|
#+end_src
|
|
|
|
** Require final newline
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq require-final-newline t)
|
|
#+end_src
|
|
|
|
** Keep Emacs custom settings in a seperate file
|
|
|
|
This includes creating the file if not existent.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
|
|
(unless (file-exists-p custom-file)
|
|
(with-temp-buffer (write-file custom-file)))
|
|
(load custom-file)
|
|
#+end_src
|
|
|
|
** Always follow VCS symlinks
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq vc-follow-symlink t)
|
|
#+end_src
|
|
|
|
** Indent using spaces, not tabs
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default indent-tabs-mode nil)
|
|
#+end_src
|
|
|
|
** Save minibuffer histroy
|
|
|
|
#+begin_src emacs-lisp
|
|
(savehist-mode 1)
|
|
#+end_src
|
|
|
|
** Auto-revert externally changed files
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-auto-revert-mode t)
|
|
(setq global-auto-revert-non-file-buffers t)
|
|
#+end_src
|
|
|
|
** Save cursor place in file for next visit
|
|
|
|
#+begin_src emacs-lisp
|
|
(save-place-mode t)
|
|
#+end_src
|
|
|
|
** Spell checking
|
|
|
|
Enable aspell. Options set according to [[https://www.emacswiki.org/emacs/FlySpell][Emacswiki: FlySpell]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default ispell-program-name "aspell")
|
|
(setq ispell-list-command "--list")
|
|
#+end_src
|
|
|
|
For using spell checking on Microsoft(R) Windows(R) a little bit more work is apparently needed. First of all some spell checking program is needed and when trying to install either ispell, aspell or hunspell I run into the problem that all three of them are primarily developed for Linux systems.
|
|
|
|
For installing aspell and the required dictionaries I decided to use [[https://www.msys2.org/][MSYS2]]. After installing this and following the initial setup guide on their website I installed aspell using =pacman -S mingw-w64-x86_64-aspell= in an MSYS2 terminal (for the next steps it was also necessary to install the =make= program with =pacman -S make=). The installation itself does not make the program available to my Emacs installation (see [[*A few words on setup][A few words on setup]]) but I needed to add the =C:\msys64\mingw64\bin= directory to the Windows =Path= variable. Therefore I opened an explorer (=WIN+e=), right-clicked "This PC" and then properties. In the appearing window I continued by clicking "Advanced system settings" in the sidebar. In the now appearing window I continued by clicking the "Environment Variables..." button. There I edited the =Path= *system* variable. By clicking on "New" first and then clicking on "Browse..." I was able to add the previously named directory to the path. Afterwards it is possible to check weather this was successful by either opening a PowerShell or CMD prompt and executing =aspell --version=.
|
|
|
|
While Emacs now finds aspell (at least after a restart of Emacs) it does not find any dictionaries and therefore not work. This is quite understandable since by default non are installed. Apparently compared to some Linux distributions MSYS2 does not provide any dictionaries through their repositories. So I downloaded the ones I needed from the [[https://ftp.gnu.org/gnu/aspell/dict/0index.html][aspell dictionaries website]]. Using the *MSYS2 MinGW 64-bit* terminal (this is important, the MSYS MSYS2 one does not work for this) I could extract them with =tar xjf aspell6-en-2020.12.07-0.tar.bz2= and switch to the extracted directory. Following the provided README I first run =./configure= and the executed =make=. Since the =make install= step fails by default I needed to adjust the Makefile by deleting the *two* lines that change the directory and execute =chmod 664= in the =install= rule. Afterwards running =make install= succeeded without any errors.
|
|
|
|
After re-visiting the current file or opening another one spell checking in Emacs works!
|
|
|
|
** scratch-buffer
|
|
|
|
I prefer that the mode of my scratch buffer is org and don't want any initial lines.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq initial-scratch-message nil)
|
|
(setq initial-major-mode 'org-mode)
|
|
#+end_src
|
|
|
|
** doc-view
|
|
|
|
Set doc-view resolution to 300 for sharp PDFs.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq doc-view-resolution 300)
|
|
#+end_src
|
|
|
|
** Raise emacsclient frame on open
|
|
|
|
Raise new ~emacsclient~ frames when created.
|
|
|
|
Source: [[https://gitlab.com/bkhl/dotfiles/-/blob/master/.emacs.d/configuration.org][bkhl's dotfiles]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/focus-frame ()
|
|
(select-frame-set-input-focus (selected-frame)))
|
|
(add-hook 'server-switch-hook 'mmk2410/focus-frame)
|
|
(add-hook 'server-after-make-frame-hook 'mmk2410/focus-frame)
|
|
#+end_src
|
|
|
|
** Enable electric pair mode
|
|
|
|
=electric-pair-mode= enables adding closing parenthesis & co when opening one.
|
|
|
|
#+begin_src emacs-lisp
|
|
(electric-pair-mode 1)
|
|
#+end_src
|
|
|
|
** camel-case words as separate words
|
|
|
|
In many programming languages writing camel-case is often used. So I would like to tread them as separate words.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'prog-mode-hook 'subword-mode)
|
|
#+end_src
|
|
|
|
** Make scripts executable when saving
|
|
|
|
Files that start with =#!= should be made executable on save.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'after-save-hook
|
|
'executable-make-buffer-file-executable-if-script-p)
|
|
#+end_src
|
|
|
|
** Follow symlinks automatically
|
|
|
|
When visiting a symlink, follow it automatically.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq vc-follow-symlinks t)
|
|
#+end_src
|
|
|
|
** Dired customizations
|
|
|
|
Show filesize human readable.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default dired-listing-switches "-alh")
|
|
#+end_src
|
|
|
|
** M-x customizations
|
|
|
|
Hide commands in M-x which do not work in the current mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq read-extended-command-predicate
|
|
#'command-completion-default-include-p)
|
|
#+end_src
|
|
|
|
* Package configuration
|
|
** General configuration
|
|
|
|
Add relevant archives.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t)
|
|
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
|
|
#+end_src
|
|
|
|
Configure the package manager to prefer newer packages and to not load packages automatically on startup.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default load-prefer-newer t)
|
|
(setq-default package-enable-at-startup nil)
|
|
#+end_src
|
|
|
|
** use-package
|
|
|
|
Install/enable use-package.
|
|
|
|
#+begin_src emacs-lisp
|
|
(unless (package-installed-p 'use-package)
|
|
(package-refresh-contents)
|
|
(package-install 'use-package))
|
|
(require 'use-package)
|
|
#+end_src
|
|
|
|
Configure use-package to load packages by default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq use-package-always-ensure t)
|
|
#+end_src
|
|
|
|
* Visual appearance
|
|
|
|
I don't agree with some visual defaults, so I redefine them.
|
|
|
|
** Short yes/no questions
|
|
|
|
#+begin_src emacs-lisp
|
|
(defalias 'yes-or-no-p 'y-or-n-p)
|
|
#+end_src
|
|
|
|
** Confirm before closing emacs
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq confirm-kill-emacs #'y-or-n-p)
|
|
#+end_src
|
|
|
|
** Disable the startup screen
|
|
|
|
Source: [[https://stackoverflow.com/a/744681][Stack Overflow: Unable to hide welcome screen in Emacs]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq inhibit-startup-screen t)
|
|
#+end_src
|
|
|
|
** Replace BEEP by visual-bell
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq visible-bell nil)
|
|
#+end_src
|
|
|
|
** Disable cursor blinking
|
|
|
|
#+begin_src emacs-lisp
|
|
(blink-cursor-mode -1)
|
|
#+end_src
|
|
|
|
** Highlight current line
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'text-mode-hook #'hl-line-mode)
|
|
(add-hook 'prog-mode-hook #'hl-line-mode)
|
|
(add-hook 'org-agenda-finalize-hook #'hl-line-mode)
|
|
#+end_src
|
|
|
|
** Show line numbers
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'text-mode-hook #'display-line-numbers-mode)
|
|
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
|
|
#+end_src
|
|
|
|
And disable this for some modes.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'elfeed-show-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
(add-hook 'eshell-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
(add-hook 'pdf-view-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
(add-hook 'shell-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
(add-hook 'eww-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
#+end_src
|
|
|
|
But when Emacs shows line numbers, they should be relative. Why? Ever tried =20j= in Evil mode or =C-u 20 C-p= in Emacs?
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq display-line-numbers-type 'relative)
|
|
#+end_src
|
|
|
|
** Replace selected text by typing
|
|
|
|
#+begin_src emacs-lisp
|
|
(delete-selection-mode t)
|
|
#+end_src
|
|
|
|
** Vertical bar as cursor
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default cursor-type 'bar)
|
|
#+end_src
|
|
|
|
** Hide tool bar, menu bar and scroll bar
|
|
|
|
I get scroll bars when using Emacs with emacsclient. So I disable the =scroll-bar-mode= explicitly when creating a new frame, but only on graphical systems. The same fix is also applied for the typeface.
|
|
|
|
#+begin_src emacs-lisp
|
|
(when (fboundp 'tool-bar-mode)
|
|
(tool-bar-mode -1))
|
|
(when (fboundp 'menu-bar-mode)
|
|
(menu-bar-mode -1))
|
|
(when (fboundp 'scroll-bar-mode)
|
|
(scroll-bar-mode -1))
|
|
#+end_src
|
|
|
|
** Highlight matching parenthesis
|
|
|
|
#+begin_src emacs-lisp
|
|
(show-paren-mode 1)
|
|
#+end_src
|
|
|
|
** Fix wrong Emacs size in some window managers
|
|
|
|
I have the problem in BSPWM that Emacs always starts floating and does not obey my rule to start in tiling mode.
|
|
|
|
Setting =frame-resize-pixelwise= to non-nil (as described in [[https://github.com/baskerville/bspwm/issues/551][GitHub: baskerville/bspwm #551]]) solves this problem.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq frame-resize-pixelwise t)
|
|
#+end_src
|
|
|
|
** Set typeface
|
|
|
|
My typeface is not loading when using emacsclient. So I set it explicitly when creating a frame. The same fix is applied for the scroll bars.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if on-macos
|
|
(setq mmk2410/frame-font "Berkeley Mono 14"
|
|
mmk2410/frame-font-present "Berkeley Mono 16"
|
|
mmk2410/fixed-font-name "Berkeley Mono"
|
|
mmk2410/fixed-font-height 140
|
|
mmk2410/fixed-font-present-height 160
|
|
mmk2410/variable-font-name "Open Sans"
|
|
mmk2410/variable-font-height 160
|
|
mmk2410/variable-font-present-height 180)
|
|
(setq mmk2410/frame-font "Berkeley Mono 11"
|
|
mmk2410/frame-font-present "Berkeley Mono 16"
|
|
mmk2410/fixed-font-name "Berkeley Mono"
|
|
mmk2410/fixed-font-height 110
|
|
mmk2410/fixed-font-present-height 160
|
|
mmk2410/variable-font-name "Open Sans"
|
|
mmk2410/variable-font-height 130
|
|
mmk2410/variable-font-present-height 160))
|
|
|
|
(add-hook 'after-make-frame-functions
|
|
#'(lambda (frame)
|
|
(set-frame-font mmk2410/frame-font nil t)))
|
|
|
|
(defun mmk2410/display-normal-typeface-size ()
|
|
(interactive)
|
|
(set-frame-font mmk2410/frame-font nil t)
|
|
(set-face-attribute 'default nil :font mmk2410/fixed-font-name :height mmk2410/fixed-font-height :weight 'regular)
|
|
(set-face-attribute 'variable-pitch nil :font mmk2410/variable-font-name :height mmk2410/variable-font-height :weight 'regular)
|
|
(set-face-attribute 'fixed-pitch nil :font mmk2410/fixed-font-name :height mmk2410/fixed-font-height :weight 'regular))
|
|
|
|
(mmk2410/display-normal-typeface-size)
|
|
#+end_src
|
|
|
|
Sometimes (most often a work) I share my screen during a video call to discuss some code. Since I personally like to see larger fonts on video calls and I don't know what display size and resolution the other participants have I wrote two functions for resizing Emacs.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/display-present-typeface-size ()
|
|
(interactive)
|
|
(set-frame-font mmk2410/frame-font-present nil t)
|
|
(set-face-attribute 'default nil :font mmk2410/fixed-font-name :height mmk2410/fixed-font-present-height :weight 'regular)
|
|
(set-face-attribute 'variable-pitch nil :font mmk2410/variable-font-name :height mmk2410/variable-font-present-height :weight 'regular)
|
|
(set-face-attribute 'fixed-pitch nil :font mmk2410/fixed-font-name :height mmk2410/fixed-font-present-height :weight 'regular))
|
|
#+end_src
|
|
|
|
Additionally like to adjust some faces to my personal liking.
|
|
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'elfeed
|
|
(set-face-attribute 'message-header-subject nil
|
|
:font mmk2410/variable-font-name
|
|
:height 220))
|
|
#+end_src
|
|
|
|
** Set theme
|
|
|
|
Installing and enabling Gruvbox light theme. /Disabled in favor of the Doom themes./
|
|
|
|
Source: [[https://github.com/greduan/emacs-theme-gruvbox][Github: greduan/emacs-theme-gruvbox]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package gruvbox-theme
|
|
:disabled t
|
|
:config
|
|
(load-theme 'gruvbox-light-hard t))
|
|
#+end_src
|
|
|
|
Installing and enabling Modus themes. /Disabled in favor of the Doom themes./
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package modus-themes
|
|
:disabled t
|
|
:init
|
|
(modus-themes-load-themes)
|
|
(setq modus-themes-bold-constructs t)
|
|
(setq modus-themes-syntax 'alt-syntax)
|
|
(setq modus-themes-slanted-constructs t)
|
|
(setq modus-themes-org-blocks 'grayscale)
|
|
(setq modus-themes-variable-pitch-headings t)
|
|
:config
|
|
(modus-themes-load-operandi))
|
|
#+end_src
|
|
|
|
Installing and enabling Doom themes.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package doom-themes
|
|
:disabled t
|
|
:config
|
|
(setq doom-themes-enable-bold t
|
|
doom-themes-enable-italic t)
|
|
(load-theme 'doom-one-light t)
|
|
(doom-themes-visual-bell-config)
|
|
(doom-themes-org-config))
|
|
#+end_src
|
|
|
|
Installing and enabling ef-themes.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ef-themes
|
|
:init
|
|
(setq ef-themes-to-toggle '(ef-light ef-dark))
|
|
(setq ef-themes-mixed-fonts t)
|
|
(setq ef-themes-headings
|
|
'((0 . (variable-pitch light 1.9))
|
|
(1 . (variable-pitch light 1.8))
|
|
(2 . (variable-pitch regular 1.7))
|
|
(3 . (variable-pitch regular 1.6))
|
|
(4 . (variable-pitch regular 1.5))
|
|
(5 . (variable-pitch 1.4))
|
|
(6 . (variable-pitch 1.3))
|
|
(7 . (variable-pitch 1.2))
|
|
(t . (variable-pitch 1.1))))
|
|
:config
|
|
(load-theme 'ef-light t))
|
|
#+end_src
|
|
|
|
|
|
Add a slight transparency to the frames. Code provided by David Wilson / System Crafters [[https://github.com/daviwil/emacs-from-scratch/blob/master/Emacs.org][Emacs from Scratch]] video series.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/alpha-background 95)
|
|
|
|
(set-frame-parameter nil 'alpha-background mmk2410/alpha-background)
|
|
(add-to-list 'default-frame-alist `(alpha-background . ,mmk2410/alpha-background))
|
|
#+end_src
|
|
|
|
** Switch theme
|
|
|
|
During the last weeks I got used to using a light theme during daytime and a dark theme during nighttime. Luckily the themes based on Atom One are to my liking and the doom package provides a dark and light variant. One thing that was difficult with Emacs was actually switching theme since it involved disabling all current ones (for some reason on one machine there are two enabled while on the other machine its only one) and then loading the correct one. These functions try to make that process easier, at least for now. The long-term goal is to automatically change the theme based on the current system-wide setting.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/theme-night 'ef-dark)
|
|
(setq mmk2410/theme-day 'ef-light)
|
|
|
|
(defun mmk2410/switch-theme (theme)
|
|
(mapcar 'disable-theme custom-enabled-themes)
|
|
(load-theme theme t)
|
|
(unless (string-prefix-p "ef-" (symbol-name theme))
|
|
(mmk2410/org-font-adjust-headlines)
|
|
(mmk2410/org-font-adjust-variable-pitch)))
|
|
|
|
(defun mmk2410/switch-theme-night ()
|
|
(interactive)
|
|
(mmk2410/switch-theme mmk2410/theme-night))
|
|
|
|
(defun mmk2410/switch-theme-day ()
|
|
(interactive)
|
|
(mmk2410/switch-theme mmk2410/theme-day))
|
|
#+end_src
|
|
|
|
** Customize modeline
|
|
|
|
I'd like to use telephone line as a mode line.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package telephone-line
|
|
:disabled t
|
|
:init (telephone-line-mode t))
|
|
#+end_src
|
|
|
|
I'm currently trying doom-modeline instead of telephone-line.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package doom-modeline
|
|
:disabled t
|
|
:init (doom-modeline-mode 1)
|
|
:config (setq doom-modeline-icon t))
|
|
#+end_src
|
|
|
|
Additionally to that I also like to know, in which column I am.
|
|
|
|
#+begin_src emacs-lisp
|
|
(column-number-mode 1)
|
|
#+end_src
|
|
|
|
** Customize tab-bar
|
|
|
|
Since version 27 Emacs has a new tab-bar feature for keeping multiple window configurations open in different tabs. By default each tab has a close button and the tab bar includes an add button. I prefer not having either one.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq tab-bar-close-button-show nil
|
|
tab-bar-new-button-show nil)
|
|
#+end_src
|
|
|
|
** Emojis
|
|
|
|
Minor mode for displaying emojis in Emacs.
|
|
|
|
Source: [[https://github.com/iqbalansari/emacs-emojify][iqbalansari/emacs-emojify]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package emojify
|
|
:ensure t
|
|
:hook (erc-mode . emojify-mode)
|
|
:commands emojify-mode)
|
|
#+end_src
|
|
|
|
** Spacious Padding
|
|
|
|
Increase the padding/spacing of GNU Emacs frames and windows
|
|
|
|
Source: [[https://git.sr.ht/~protesilaos/spacious-padding][sr.ht: ~protesilaos/spacious-padding]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package spacious-padding
|
|
:config (spacious-padding-mode 1))
|
|
#+end_src
|
|
|
|
** Line Spacing
|
|
|
|
I found out that a bit more line spacing makes Emacs look more nicely and increases the readability a bit. At least for me 🙂
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default line-spacing 4)
|
|
#+end_src
|
|
|
|
* Keybinding
|
|
|
|
List of own custom keybindings
|
|
|
|
** Emacs Lisp related
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-c x") 'eval-region)
|
|
#+end_src
|
|
|
|
** Ibuffer as default
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-x C-b") 'ibuffer)
|
|
#+end_src
|
|
|
|
** Disable =C-z=
|
|
|
|
=C-z= by default (as known from terminal applications) suspends the current frame. Since I use Emacs as GUI and not in terminal I do not want this behavior.
|
|
|
|
#+begin_src emacs-lisp
|
|
(unbind-key "C-z")
|
|
#+end_src
|
|
|
|
** The evil side
|
|
|
|
I currently try out =evil-mode= for evaluating if it is any better for my hand.
|
|
|
|
Source: [[https://github.com/emacs-evil/evil][GitHub: emacs-evil/evil]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil
|
|
:disabled t
|
|
:init
|
|
(setq evil-want-integration t)
|
|
(setq evil-want-keybinding nil)
|
|
:config
|
|
(evil-mode 1)
|
|
(define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
|
|
(define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)
|
|
(evil-global-set-key 'motion "j" 'evil-next-visual-line)
|
|
(evil-global-set-key 'motion "k" 'evil-previous-visual-line))
|
|
#+end_src
|
|
|
|
An even better integration may be achieved when additionall using the [[https://github.com/emacs-evil/evil-collection][evil-collection]] package.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-collection
|
|
:disabled t
|
|
:after evil
|
|
:config (evil-collection-init))
|
|
#+end_src
|
|
|
|
Additional I like to press =jk= for exiting normal mode, using [[https://github.com/emacsorphanage/key-chord/][key-chord]] this is possible.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package key-chord
|
|
:disabled t
|
|
:config
|
|
(key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
|
|
(key-chord-mode 1))
|
|
#+end_src
|
|
|
|
* General packages
|
|
** diminish-mode
|
|
|
|
Diminished modes are minor modes with no modeline display
|
|
|
|
Source: [[https://github.com/myrjola/diminish.el][GitHub: myrjola/diminish.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package diminish
|
|
:init
|
|
(diminish 'abbrev-mode)
|
|
(diminish 'buffer-face-mode)
|
|
(diminish 'flyspell-mode)
|
|
(diminish 'org-indent-mode)
|
|
(diminish 'org-cdlatex-mode)
|
|
(diminish 'visual-line-mode)
|
|
(diminish 'buffer-face-mode)
|
|
(diminish 'highlight-indent-guides-mode)
|
|
(diminish 'eldoc-mode)
|
|
(diminish 'subword-mode))
|
|
#+end_src
|
|
|
|
** crux
|
|
|
|
A Collection of Ridiculously Useful eXtensions for Emacs.
|
|
|
|
Used especially for =crux-delete-file-and-buffer= and =crux-rename-file-and-buffer=
|
|
|
|
Source: [[https://github.com/bbatsov/crux][GitHub: bbatsov/crux]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package crux
|
|
:bind (:map prog-mode-map
|
|
("C-c C-." . crux-duplicate-current-line-or-region)
|
|
("C-c C-M-." . crux-duplicate-and-comment-current-line-or-region)
|
|
:map global-map
|
|
("C-c M-r" . crux-rename-file-and-buffer)))
|
|
#+end_src
|
|
|
|
** which-key
|
|
|
|
Emacs package that displays available keybindings in popup
|
|
|
|
Source: [[https://github.com/justbur/emacs-which-key][GitHub: justbur/emacs-which-key]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package which-key
|
|
:diminish which-key-mode
|
|
:init (which-key-mode)
|
|
:config (setq which-key-idle-delay 0.5))
|
|
#+end_src
|
|
|
|
** multiple-cursors
|
|
|
|
Multiple cursors for emacs.
|
|
|
|
Source: [[https://github.com/magnars/multiple-cursors.el][GitHub: magnars/multiple-cursors]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package multiple-cursors
|
|
:bind
|
|
(("C->" . mc/mark-next-like-this)
|
|
("C-<" . mc/mark-previous-like-this)
|
|
("C-*" . mc/mark-all-like-this)
|
|
("C-;" . mc/edit-lines)))
|
|
#+end_src
|
|
|
|
** magit
|
|
|
|
A Git Porcelain inside Emacs
|
|
|
|
Source: [[https://magit.vc/][magit.vc]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package magit
|
|
:bind
|
|
("C-x g" . magit-status))
|
|
#+end_src
|
|
|
|
** orgit
|
|
|
|
Support for Org links to Magit buffers.
|
|
|
|
- Source :: [[https://github.com/magit/orgit][GitHub: magit/orgit]]
|
|
- Blog post :: [[https://mmk2410.org/2022/01/21/my-emacs-package-of-the-week-orgit/][My Emacs package of the week: orgit]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package orgit
|
|
:after (magit org)
|
|
:config
|
|
(add-to-list 'orgit-export-alist
|
|
'("git.mmk2410.org[:/]\\(.+?\\)\\(?:\\.git\\)?$"
|
|
"https://git.mmk2410.org/%n"
|
|
"https://git.mmk2410.org/%n/commits/commit/%r"
|
|
"https://git.mmk2410.org/%n/commit/%r")))
|
|
#+end_src
|
|
|
|
** gitmoji
|
|
|
|
Insert gitmoji in Emacs.
|
|
|
|
Source: [[https://github.com/janusvm/emacs-gitmoji][GitHub: janusvm/emacs-gitmoji]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package gitmoji
|
|
:load-path "lisp/gitmoji/")
|
|
#+end_src
|
|
|
|
** diff-hl
|
|
|
|
Emacs package for highlighting uncommitted changes
|
|
|
|
Source: [[https://github.com/dgutov/diff-hl][Github: dgutov/diff-hl]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package diff-hl
|
|
:config (global-diff-hl-mode t)
|
|
:hook (magit-post-refresh-hook . diff-hl-magit-post-refresh))
|
|
#+end_src
|
|
|
|
** project.el
|
|
|
|
Load the included =project= package.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package project)
|
|
#+end_src
|
|
|
|
** treemacs
|
|
|
|
Treemacs is a package for displaying a directory tree in a (side) buffer. While browsing a project using =project.el= is also possible and perhaps fast, I think that the tree approach especially helps discovering a new project.
|
|
|
|
Source: [[https://github.com/Alexander-Miller/treemacs][Alexander-Miller/treemacs]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package treemacs
|
|
:defer t
|
|
:config
|
|
(setq treemacs-litter-directories '("/node_modules" "/.venv" "/.cask" "/vendor")
|
|
treemacs-indent-guide-style 'line)
|
|
(treemacs-follow-mode t)
|
|
(treemacs-git-mode 'deferred)
|
|
(treemacs-indent-guide-mode t)
|
|
:bind (:map global-map
|
|
("M-0" . treemacs-select-window)
|
|
("C-c b 1" . treemacs-delete-other-windows)
|
|
("C-c b b" . treemacs)
|
|
("C-c b B" . treemacs-bookmark)
|
|
("C-c b C-f" . treemacs-find-file)
|
|
("C-c b M-f" . treemacs-find-tag)))
|
|
|
|
(use-package treemacs-evil
|
|
:disabled t
|
|
:after (treemacs evil))
|
|
#+end_src
|
|
|
|
** vertico
|
|
|
|
Vertico is a minimal completion UI that uses core Emacs functionality whenever possible.
|
|
|
|
[[https://github.com/minad/vertico][GitHub: minad/vertico]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package vertico
|
|
:init
|
|
(vertico-mode)
|
|
(setq vertico-scroll-margin 0)
|
|
(setq vertico-resize t)
|
|
(setq vertico-cycle t)
|
|
;; Hide commands in M-x which do not work in the current mode.
|
|
(setq read-extended-command-predicate
|
|
#'command-completion-default-include-p))
|
|
#+end_src
|
|
|
|
** orderless
|
|
|
|
Emacs completion style that matches multiple regexps in any order
|
|
|
|
[[https://github.com/oantolin/orderless][GitHub: oantolin/orderless]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package orderless
|
|
:ensure t
|
|
:custom
|
|
(completion-styles '(orderless basic))
|
|
(completion-category-overrides '((file (styles basic partial-completion)))))
|
|
#+end_src
|
|
|
|
** marginalia
|
|
|
|
Marginalia in the minibuffer.
|
|
|
|
[[https://github.com/minad/marginalia][GitHub: minad/marginalia]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package marginalia
|
|
:init
|
|
(marginalia-mode)
|
|
:bind (:map minibuffer-local-map
|
|
("M-A" . marginalia-cycle)))
|
|
#+end_src
|
|
|
|
** consult
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package consult
|
|
;; Replace bindings. Lazily loaded due by `use-package'.
|
|
:bind (;; C-c bindings (mode-specific-map)
|
|
("C-c h" . consult-history)
|
|
("C-c m" . consult-mode-command)
|
|
("C-c k" . consult-kmacro)
|
|
;; C-x bindings (ctl-x-map)
|
|
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
|
|
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
|
|
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
|
|
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
|
|
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
|
|
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
|
|
;; Custom M-# bindings for fast register access
|
|
("M-#" . consult-register-load)
|
|
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
|
|
("C-M-#" . consult-register)
|
|
;; Other custom bindings
|
|
("M-y" . consult-yank-pop) ;; orig. yank-pop
|
|
;; M-g bindings (goto-map)
|
|
("M-g e" . consult-compile-error)
|
|
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
|
|
("M-g g" . consult-goto-line) ;; orig. goto-line
|
|
("M-g M-g" . consult-goto-line) ;; orig. goto-line
|
|
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
|
|
("M-g m" . consult-mark)
|
|
("M-g k" . consult-global-mark)
|
|
("M-g i" . consult-imenu)
|
|
("M-g I" . consult-imenu-multi)
|
|
;; M-s bindings (search-map)
|
|
("M-s d" . consult-find)
|
|
("M-s D" . consult-locate)
|
|
("M-s g" . consult-grep)
|
|
("M-s G" . consult-git-grep)
|
|
("M-s r" . consult-ripgrep)
|
|
("M-s l" . consult-line)
|
|
("M-s L" . consult-line-multi)
|
|
("M-s m" . consult-multi-occur)
|
|
("M-s k" . consult-keep-lines)
|
|
("M-s u" . consult-focus-lines)
|
|
;; Isearch integration
|
|
("M-s e" . consult-isearch-history)
|
|
:map isearch-mode-map
|
|
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
|
|
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
|
|
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
|
|
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
|
|
;; Minibuffer history
|
|
:map minibuffer-local-map
|
|
("M-s" . consult-history) ;; orig. next-matching-history-element
|
|
("M-r" . consult-history)) ;; orig. previous-matching-history-element
|
|
:hook (completion-list-mode . consult-preview-at-point-mode)
|
|
:init
|
|
(setq register-preview-delay 0.5
|
|
register-preview-function #'consult-register-format)
|
|
(advice-add #'register-preview :override #'consult-register-window)
|
|
(setq xref-show-xrefs-function #'consult-xref
|
|
xref-show-definitions-function #'consult-xref)
|
|
:config
|
|
(consult-customize
|
|
consult-theme :preview-key '(:debounce 0.2 any)
|
|
consult-ripgrep consult-git-grep consult-grep
|
|
consult-bookmark consult-recent-file consult-xref
|
|
consult--source-bookmark consult--source-file-register
|
|
consult--source-recent-file consult--source-project-recent-file
|
|
:preview-key '(:debounce 0.4 any))
|
|
(setq consult-narrow-key "<"))
|
|
#+end_src
|
|
|
|
** embark
|
|
|
|
Emacs Mini-Buffer Actions Rooted in Keymaps
|
|
|
|
[[https://github.com/oantolin/embark][GitHub: oantolin/embark]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package embark
|
|
:bind
|
|
(("C-." . embark-act) ;; pick some comfortable binding
|
|
("C-;" . embark-dwim) ;; good alternative: M-.
|
|
("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
|
|
|
|
:init
|
|
(setq prefix-help-command #'embark-prefix-help-command)
|
|
:config
|
|
(add-to-list 'display-buffer-alist
|
|
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
|
|
nil
|
|
(window-parameters (mode-line-format . none)))))
|
|
|
|
(use-package embark-consult
|
|
:hook
|
|
(embark-collect-mode . consult-preview-at-point-mode))
|
|
#+end_src
|
|
|
|
** avy
|
|
|
|
Jump to things in Emacs tree-style
|
|
|
|
Source: [[https://github.com/abo-abo/avy][GitHub: abo-abo/avy]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package avy
|
|
:bind
|
|
(("C-:" . avy-goto-char)
|
|
("C-M-:" . avy-goto-char-2)))
|
|
#+end_src
|
|
|
|
** yasnippet
|
|
|
|
YASnippet is a template system for Emacs.
|
|
|
|
Snippets can be put in the ~yas-snippet-dirs~, e.g. =~/.emacs.d/snippets=.
|
|
|
|
Source: [[https://github.com/joaotavora/yasnippet][GitHub: joaotavora/yasnippet]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yasnippet
|
|
:diminish 'yas-minor-mode
|
|
:config (yas-global-mode 1))
|
|
#+end_src
|
|
|
|
** pdf-tools
|
|
|
|
An advanced Emacs support library for PDF files.
|
|
|
|
Source: [[https://github.com/vedang/pdf-tools][GitHub: vedang/pdf-tools]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package pdf-tools
|
|
:if (not (eq system-type 'windows-nt))
|
|
:config
|
|
(pdf-loader-install)
|
|
;; open pdfs scaled to fit page
|
|
(setq-default pdf-view-display-size 'fit-page)
|
|
;; more fine-grained zooming
|
|
(setq pdf-view-resize-factor 1.1)
|
|
;; use normal isearch
|
|
(define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
|
|
(define-key pdf-view-mode-map (kbd "C-r") 'isearch-backward)
|
|
;; keyboard shortcuts
|
|
(define-key pdf-view-mode-map (kbd "h") 'pdf-annot-add-highlight-markup-annotation)
|
|
(define-key pdf-view-mode-map (kbd "t") 'pdf-annot-add-text-annotation)
|
|
(define-key pdf-view-mode-map (kbd "D") 'pdf-annot-delete))
|
|
#+end_src
|
|
|
|
** deft
|
|
|
|
Deft is a nice tool for searching for files (in my case my notes). Since it searches not only through the title of files but also through the contents it may be useful for me.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package deft
|
|
:bind ("C-c d" . deft)
|
|
:commands (deft)
|
|
:config (setq deft-directory "~/org"
|
|
deft-extensions '("org")
|
|
deft-recursive t))
|
|
#+end_src
|
|
|
|
** ace-window
|
|
|
|
Quickly switch windows in Emacs
|
|
|
|
Source: [[https://github.com/abo-abo/ace-window][GitHub: abo-abo/ace-window]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ace-window
|
|
:bind (("M-o" . 'ace-window)))
|
|
#+end_src
|
|
|
|
* Org mode configuration
|
|
|
|
This is my personal org-mode configuration.
|
|
|
|
** Org modules
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:pin gnu)
|
|
(add-to-list 'org-modules 'org-habit)
|
|
(require 'org-protocol)
|
|
#+end_src
|
|
|
|
** General settings
|
|
|
|
Follow links when pressing enter on them.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-return-follows-link t)
|
|
#+end_src
|
|
|
|
Disable extra indent in src-blocks.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-edit-src-content-indentation 0)
|
|
#+end_src
|
|
|
|
Prevent accidental edits in invisible regions.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-catch-invisible-edits 'error)
|
|
#+end_src
|
|
|
|
Replace the three dots for collapsed stuff with a nicer symbol.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-ellipsis " ▼")
|
|
#+end_src
|
|
|
|
Hide emphasis markers. While this sometimes is very distracting it mostly looks absolutely beautiful.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-hide-emphasis-markers t)
|
|
#+end_src
|
|
|
|
** Org font adjustments
|
|
|
|
I like to have larger headlines in Org for better identifying them. The configuration of [[https://config.daviwil.com/emacs#fonts-and-bullets][David Wilson]] was a large help in this regard.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/org-font-adjust-headlines ()
|
|
"Adjust headline sizes, font family and weight"
|
|
(dolist (face '((org-level-1 . 1.4)
|
|
(org-level-2 . 1.3)
|
|
(org-level-3 . 1.2)
|
|
(org-level-4 . 1.15)
|
|
(org-level-5 . 1.1)
|
|
(org-level-6 . 1.1)
|
|
(org-level-7 . 1.1)
|
|
(org-level-8 . 1.1)))
|
|
(set-face-attribute (car face) nil :font "Open Sans" :weight 'bold :height (cdr face))))
|
|
|
|
(with-eval-after-load 'org
|
|
(unless (string-prefix-p "ef-" (format "%s" (car custom-enabled-themes)))
|
|
(mmk2410/org-font-adjust-headlines)))
|
|
#+end_src
|
|
|
|
When using a variable-pitch font this also applies to stuff like code blocks. The following block resets this change for these unwanted faces.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/org-font-adjust-variable-pitch ()
|
|
"Re-delare certain org font as fixed pitch when using variable pitch mode."
|
|
(org-indent-mode)
|
|
(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch)
|
|
(set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
|
|
(set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch))
|
|
(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
|
|
(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
|
|
(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
|
|
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)
|
|
(set-face-attribute 'org-table nil :inherit 'fixed-pitch))
|
|
|
|
(with-eval-after-load 'org
|
|
(unless (string-prefix-p "ef-" (format "%s" (car custom-enabled-themes)))
|
|
(mmk2410/org-font-adjust-variable-pitch)))
|
|
#+end_src
|
|
|
|
** Org Superstar Mode
|
|
|
|
=org-superstar-mode= is a nice package for enriching the display of heading stars as well as list items.
|
|
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'org
|
|
(use-package org-superstar
|
|
:hook (org-mode . org-superstar-mode)))
|
|
#+end_src
|
|
|
|
** Org Appear Mode
|
|
|
|
Toggle visibility of hidden Org mode element parts upon entering and leaving an element.
|
|
|
|
- Source :: [[https://github.com/awth13/org-appear][GitHub: awth13/org-appear]]
|
|
- Blog Post :: [[https://mmk2410.org/2022/02/05/my-emacs-package-of-the-week-org-appear/][My Emacs package of the week: org-appear]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-appear
|
|
:after org
|
|
:hook (org-mode . org-appear-mode))
|
|
#+end_src
|
|
|
|
** LaTeX-related settings
|
|
|
|
Add =scratcl= KOMA-Script class as available LaTeX class.
|
|
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'ox-latex
|
|
(add-to-list 'org-latex-classes
|
|
'("scrartcl"
|
|
"\\documentclass{scrartcl}"
|
|
("\\section{%s}" . "\\section*{%s}")
|
|
("\\subsection{%s}" . "\\subsection*{%s}")
|
|
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
|
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
|
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
|
|
#+end_src
|
|
|
|
Set the scrartcl class as default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-default-class "scrartcl")
|
|
#+end_src
|
|
|
|
Us LuaLaTeX as default LaTeX compiler.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-compiler "lualatex")
|
|
#+end_src
|
|
|
|
Customize LaTeX packages and configurations.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-packages-alist
|
|
'(
|
|
("" "booktabs" t)
|
|
("" "amsmath" t)
|
|
("AUTO" "polyglossia" t)
|
|
("colorlinks" "hyperref" t)
|
|
"\\setmainfont{Linux Libertine O}"
|
|
"\\setsansfont{Linux Biolinum O}"
|
|
"\\setmonofont[Scale=0.9]{JetBrainsMono}"))
|
|
#+end_src
|
|
|
|
Configure minted source code listings for LaTeX export. For actually using the configuration it is necessary to set =org-latex-listings= to ='minted= using a local variable and to add the =minted= and =xcolor= package to =org-latex-packages-alist=. This is not done by default since I don't need it for all files and it requires =--shell-escape= for the LaTeX compile process.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-minted-options
|
|
'(("baselinestretch" "1.2")
|
|
("breaklines" "true")
|
|
("fontsize" "\\footnotesize")
|
|
("frame" "single")
|
|
("framesep" "2mm")
|
|
("linenos" "true")
|
|
("style" "pastie")))
|
|
#+end_src
|
|
|
|
Removed unused default packages.
|
|
|
|
#+begin_src emacs-lisp
|
|
(unless (boundp 'org-latex-default-packages-alist)
|
|
(setq org-latex-default-packages-alist nil))
|
|
(setq
|
|
org-latex-default-packages-alist (remove '("AUTO" "inputenc" t) org-latex-default-packages-alist)
|
|
org-latex-default-packages-alist (remove '("" "fixltx2e" nil) org-latex-default-packages-alist)
|
|
org-latex-default-packages-alist (remove '("" "hyperref" nil) org-latex-default-packages-alist)
|
|
org-latex-default-packages-alist (remove '"\\tolerance=1000" org-latex-default-packages-alist))
|
|
#+end_src
|
|
|
|
Configure the org LaTeX preview to work with LuaLaTeX.
|
|
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'org
|
|
(add-to-list 'org-preview-latex-process-alist
|
|
'(luamagick :programs
|
|
("lualatex" "convert")
|
|
:description "pdf > png" :message "you need to install the programs: lualatex and imagemagick."
|
|
:image-input-type "pdf"
|
|
:image-output-type "png"
|
|
:image-size-adjust (1.0 . 1.0)
|
|
:latex-compiler
|
|
("lualatex --interaction=nonstopmode -output-directory=%o %f")
|
|
:image-converter
|
|
("convert -density %D -trim -antialias %f -quality 100 %O"))))
|
|
|
|
(setq org-preview-latex-default-process 'luamagick)
|
|
#+end_src
|
|
|
|
Also adjust the LaTeX preview to be a little bit larger, since it is to small for me.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5))
|
|
#+end_src
|
|
|
|
** ToDo-Management and Org Agenda
|
|
|
|
Set TODO Keywords.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-todo-keywords '((sequence
|
|
"TODO(t!)" "NEXT(n!)" "STARTED(s!)" "WAITING(w@/@)"
|
|
"|" "POSTPONED(p@/!)" "DELEGATED(f@/!)" "CANCELLED(c@/!)" "DONE(d!)")))
|
|
#+end_src
|
|
|
|
Set a predefined tags. The first group is the list of GTD contexts. These are (by nature) exclusive and since a project is also an exclusive state I added it to the group even if it is not a context.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-tag-persistent-alist '((:startgroup . nil)
|
|
("@agendas" . ?a)
|
|
("@anywhere" . ?y)
|
|
("@calls" . ?t)
|
|
("@computer" . ?c)
|
|
("@errands" . ?e)
|
|
("@fathers" . ?f)
|
|
("@home" . ?h)
|
|
("@photography" . ?s)
|
|
("@read" . ?r)
|
|
("@work_computer" . ?w)
|
|
("@project" . ?p)
|
|
(:endgroup . nil)
|
|
("coding" . ?C)
|
|
("graphic_design" . ?G)
|
|
("habit" . ?H)
|
|
("kitchen" . ?K)
|
|
("photo_editing" . ?P)
|
|
("sysadmin" . ?S)))
|
|
#+end_src
|
|
|
|
Set time tag when re-something on todos
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-log-done 'time
|
|
org-log-reschedule 'time
|
|
org-log-redeadline 'time
|
|
org-log-into-drawer t)
|
|
#+end_src
|
|
|
|
Forbid closing of todos with open subtasks.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq
|
|
org-enforce-todo-dependencies t
|
|
org-enforce-todo-checkbox-dependencies t)
|
|
#+end_src
|
|
|
|
Set org agenda files.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-files (list "~/org/tasks/"))
|
|
|
|
(setq org-agenda-text-search-extra-files
|
|
(directory-files-recursively "~/org/roam" "\.org$"))
|
|
|
|
(setq org-agenda-text-search-extra-files
|
|
(append
|
|
org-agenda-text-search-extra-files
|
|
(directory-files-recursively "~/org/archive" "\.org$")))
|
|
|
|
(setq org-agenda-text-search-extra-files
|
|
(append
|
|
org-agenda-text-search-extra-files
|
|
(directory-files-recursively "~/org/articles" "\.org$")))
|
|
#+end_src
|
|
|
|
Open org-agenda in current window.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-window-setup (quote current-window))
|
|
#+end_src
|
|
|
|
Define org-capture templates.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/created-date ":PROPERTIES:\n:CREATED: %U\n:END:")
|
|
(setq org-capture-templates
|
|
`(("q" "Quick Capture" entry (file "~/org/tasks/inbox.org")
|
|
,(concat "* TODO %^{Todo Title}\n:" mmk2410/created-date "\n")
|
|
:immediate-finish t)
|
|
("p" "New Personal Project" entry (file "~/org/tasks/personal.org")
|
|
,(concat "* %? :@project:\n" mmk2410/created-date "\n"))
|
|
("t" "New Personal Task" entry (file "~/org/tasks/personal.org")
|
|
,(concat "* %? %^g\n" mmk2410/created-date "\n"))
|
|
("P" "New Work Project" entry (file "~/org/tasks/work.org")
|
|
,(concat "* %? :@project:\n" mmk2410/created-date "\n"))
|
|
("T" "New Work Task" entry (file "~/org/tasks/work.org")
|
|
,(concat "* %? %^g\n" mmk2410/created-date "\n"))
|
|
("r" "New Tickler Entry" entry (file "~/org/tasks/tickler.org")
|
|
,(concat "* %?\n" mmk2410/created-date "\n%^{Remind me on}t\n"))
|
|
("e" "E-Mail needing a reply" entry (file "~/org/tasks/inbox.org")
|
|
,(concat "* TODO Reply: %a\n" mmk2410/created-date "\n")
|
|
:immediate-finish t)
|
|
("p" "org-protocol-capture" entry (file "~/org/tasks/inbox.org")
|
|
,(concat "* TODO [[%:link][%:description]]\n" mmk2410/created-date "\n\n%i")
|
|
:immediate-finish t)))
|
|
#+end_src
|
|
|
|
Configure refiling TODOs.
|
|
|
|
- Show headline to refile to as path, also including the file name
|
|
- Don't select hierarchically, but show everything at once
|
|
- Enable the ability to create parent nodes when refiling
|
|
- Set refile targets to agenda files and only consider the first and second level
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-refile-use-outline-path 'file)
|
|
(setq org-outline-path-complete-in-steps nil)
|
|
(setq org-refile-allow-creating-parent-nodes 'confirm)
|
|
(setq org-refile-targets '((org-agenda-files :maxlevel . 2)))
|
|
#+end_src
|
|
|
|
Warn me on deadlines in the next year.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-deadline-warning-days 7)
|
|
#+end_src
|
|
|
|
Org Agenda mode settings:
|
|
|
|
- Set agenda span to one day
|
|
- Don't show entries twice (scheduled and deadline)
|
|
- Start week today and not on Monday.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-span 'day)
|
|
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
|
|
(setq org-agenda-start-on-weekday nil)
|
|
#+end_src
|
|
|
|
I don't use the categories at all so I decided to remove the corresponding column.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-prefix-format
|
|
'((agenda . " %i %?-12t% s")
|
|
(todo . " %i")
|
|
(tags . " %i")
|
|
(search . " %i")))
|
|
#+end_src
|
|
|
|
By default, org-mode separates "blocks" (e.g. an agenda view and a todo view) with a horizontal line of equal signs and adding some headlines. Since I have more current tasks and projects than my screen is high I like to keep it as compact as possible.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-block-separator nil
|
|
org-agenda-compact-blocks t)
|
|
#+end_src
|
|
|
|
For determining the current state of my tasks I found it helpful to not only define one org-agenda view and use it always (the "Agenda" view with the shortcut "o" was in a very similar form my default for years). That's why I decided to define additional more precise lists for todays and upcoming tasks as well as for on overview of started, waiting and next tasks.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-agenda-custom-commands
|
|
'(("o" "Agenda"
|
|
((todo "STARTED")
|
|
(agenda "")
|
|
(todo "NEXT")))
|
|
("u" "Upcoming tasks"
|
|
agenda "" ((org-agenda-span 'week)))
|
|
("d" "Today tasks"
|
|
agenda "" ((org-agenda-span 'day)))
|
|
("b" "Started tasks" todo "STARTED")
|
|
("w" "Waiting for tasks" todo "WAITING")
|
|
("n" "Next tasks" todo "NEXT")))
|
|
#+end_src
|
|
|
|
For easily choosing the next thing to work on I give each of my tasks a context (following the GTD methodology). For quickly getting a list of all actionable tasks for a context I'd like to have a quick keyboard shortcut to view a custom agenda. For this I defined a list of contexts and their shortcut and iterated over them to create an additional ~org-agenda-custom-commands~ entry for each.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/contexts
|
|
'(("@agendas" . "a")
|
|
("@anywhere" . "y")
|
|
("@calls" . "t")
|
|
("@computer" . "c")
|
|
("@errands" . "e")
|
|
("@fathers" . "f")
|
|
("@home" . "h")
|
|
("@photography" . "s")
|
|
("@read" . "r")
|
|
("@work_computer" . "w")))
|
|
|
|
(dolist (item mmk2410/contexts)
|
|
(add-to-list 'org-agenda-custom-commands
|
|
`(,(concat "c" (cdr item))
|
|
,(concat (substring (car item) 1) " context")
|
|
tags-todo
|
|
,(concat (car item) "/!+NEXT|+STARTED|+WAITING"))))
|
|
#+end_src
|
|
|
|
** Automatic UUID creation
|
|
|
|
I am currently exploring the option in Org mode to export a file (or some entries) to an ics-file which I can then in turn import into my calendar. For reliably creating (and most importantly: updating) entries it is necessary that each one has an unique ID. To create a ID for the current entry I could just run =M-x org-id-get-create= and for an entire file execute the following Emacs Lisp ~(org-map-entries 'org-id-get-create)~. Of course this is not an ideal solution. But adding this s-expression to ~org-mode-hook~ would create IDs in all Org file I edit which I also don't like. Since the amount of files I do want the automatic creation is (currently) not that large it is OK for me to do some work on my own, at least if it is only a one time setup.
|
|
|
|
The idea which I had to achieve this goal was to create a file-local variable (called ~mmk2410/org-create-uuids~) that I set to ~t~ in the buffers I need the automatic adding of IDs and adding a hook to ~org-mode-hook~ that in turn adds a hook to ~before-save-hook~ for calling the function ~mmk2410/org-create-all-uuids~ that executes the previously named s-expression to add a ID to each entry in the buffer.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/org-create-uuids nil)
|
|
|
|
(defun mmk2410/org-create-all-uuids ()
|
|
"Create UUIDs for all entries in the current org-mode buffer."
|
|
(interactive)
|
|
(when mmk2410/org-create-uuids
|
|
(org-map-entries 'org-id-get-create)))
|
|
|
|
(add-hook 'org-mode-hook
|
|
(lambda () (add-hook 'before-save-hook 'mmk2410/org-create-all-uuids nil t)))
|
|
#+end_src
|
|
|
|
** Keybindings
|
|
|
|
Fast org key bindings.
|
|
|
|
- C-c l for =org-store-link=
|
|
- C-c c for =org-capture=
|
|
- C-c o for =org-switchb=
|
|
- C-c a for =org-agenda=
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-c l") 'org-store-link)
|
|
(global-set-key (kbd "C-c c") 'org-capture)
|
|
(global-set-key (kbd "C-c o") 'org-switchb)
|
|
(global-set-key (kbd "C-c a") 'org-agenda)
|
|
#+end_src
|
|
|
|
** Nicer display for Org buffers
|
|
|
|
From [[https://github.com/daviwil/emacs-from-scratch/blob/master/Emacs.org#center-org-buffers][System Crafters' Emacs from Scratch Config]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun efs/org-mode-visual-fill ()
|
|
(setq visual-fill-column-width 100
|
|
visual-fill-column-center-text t)
|
|
(visual-fill-column-mode 1))
|
|
|
|
(use-package visual-fill-column
|
|
:diminish visual-fill-column-mode
|
|
:hook (org-mode . efs/org-mode-visual-fill))
|
|
|
|
(add-hook 'org-mode-hook (lambda ()
|
|
(display-line-numbers-mode -1)
|
|
(variable-pitch-mode)))
|
|
#+end_src
|
|
|
|
** Hooks
|
|
|
|
Enable =flyspell-mode= and disable line numbers in org-agenda.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'org-mode-hook 'flyspell-mode)
|
|
(add-hook 'org-agenda-mode-hook (lambda () (display-line-numbers-mode -1)))
|
|
#+end_src
|
|
|
|
** Org Journal
|
|
|
|
Until now I did not think that I needed to keep a (personal) journal. But since I is recommended to keep a journal of whom you met due to the corona pandemic I started thinking about setting up one. Further use cases came then to my mind and so I decided to give it a try. Therefore the configuration is still in a very early state and may change quite soon.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-journal
|
|
:config (setq org-journal-dir "~/org/journal"
|
|
org-journal-file-type 'monthly
|
|
org-journal-find-file 'find-file
|
|
org-journal-file-format "%Y-%m-%d.org"
|
|
org-journal-date-format "%A, %Y-%m-%d")
|
|
:bind (("C-c j j" . 'org-journal-new-entry)
|
|
("C-c j s" . 'org-journal-new-scheduled-entry)
|
|
("C-c j o" . 'org-journal-open-current-journal-file)
|
|
("C-c j w" . 'mmk2410/org-journal-worktime)))
|
|
#+end_src
|
|
|
|
Part of my workflow with org journal is in the meantime to clock the time I work in the different entries using =org-clock=. At the end of the day I like to create a summary for that. Currently the process for this includes a table with to formulas I created some time ago and copy every day from the previous one (while the first formula calculates the times the second is just there to clean the fields). While this is certainly improvable in many ways the only thing that really annoyed me was the calculation of the time. Since I try to follow the 52:17 methodology I have to add many numbers around 52 up to get the usual hour:minutes format. Although this is certainly a good brain training it has been shown that the calculation is very error-prone and calculating this twice does not make it more fun.
|
|
|
|
Therefore I created a little interactive helper function to sum these times up. The function =mmk2410/org-journal-worktime= reads a list of space separated minute values (so the time 1:04 must be entered as 64) and returns it as an minibuffer message in the required format (hh:mm).
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/org-journal-worktime (workmins)
|
|
"Sum up the entered minutes and return it in hh:mm format."
|
|
(interactive "Show many minutes did you work today? ")
|
|
(let ((worksum (apply '+ (mapcar 'string-to-number (split-string workmins)))))
|
|
(message "Total work time: %d:%d" (/ worksum 60) (mod worksum 60))))
|
|
#+end_src
|
|
|
|
I am quite sure that the function can be improved in various ways. I am not a Lisp hacker and have not written very much in it. But at the moment it fits my needs (and I currently do not have more time to spare on this).
|
|
|
|
** Org Drill
|
|
|
|
org-drill is a package for spaced repetition in Emacs using org-mode files.
|
|
|
|
Source: [[https://gitlab.com/phillord/org-drill/][GitLab: phillord/org-drill]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-drill
|
|
:after org
|
|
:config
|
|
(setq org-drill-learn-fraction 0.4)
|
|
(setq org-drill-maximum-items-per-session 50))
|
|
#+end_src
|
|
|
|
** Org for blogging
|
|
|
|
Since [[https://mmk2410.org/2020/05/15/switching-my-website-to-hugo-using-ox-hugo/][May 2020]] I'm using [[https://gohugo.io/][hugo]] with [[https://ox-hugo.scripter.co/][ox-hugo]] as a org-mode based static site generator for my [[https://mmk2410.org][mmk2410.org]] site. You can find the source code for the website [[https://git.mmk2410.org/mmk2410/mmk2410.org][on my Gitea instance]] (and also [[https://gitlab.com/mmk2410/mmk2410.org][GitLab]] and [[https://github.com/mmk2410/mmk2410.org][GitHub]] if you prefer that). First of all I need to include =ox-hugo= by [[https://scripter.co/][Kaushal Modi]]:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ox-hugo
|
|
:after org)
|
|
#+end_src
|
|
|
|
After I wrote an [[https://mmk2410.org/2022/01/15/improving-my-new-blog-post-creation/][own capture template]] for starting a new blog post, Kashual Modi, the creator of =ox-hugo=, made me aware of [[https://ox-hugo.scripter.co/doc/org-capture-setup/][an existing template in the documentation]] that also handles the automatic creation of the =EXPORT_FILE_NAME= property. I copied if from there and made some very slight adjustments to fit my needs.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defconst mmk2410/blog-posts-file
|
|
"~/projects/mmk2410.org/content-org/blog.org"
|
|
"Position of my org file containing all blog posts.")
|
|
|
|
;; Populates only the EXPORT_FILE_NAME property in the inserted heading.
|
|
(with-eval-after-load 'org-capture
|
|
(defun org-hugo-new-subtree-post-capture-template ()
|
|
"Returns `org-capture' template string for new Hugo post. See `org-capture-templates' for more information."
|
|
(let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
|
|
(fname (org-hugo-slug title)))
|
|
(mapconcat #'identity
|
|
`(
|
|
,(concat "* TODO " title)
|
|
":PROPERTIES:"
|
|
,(concat ":EXPORT_FILE_NAME: " fname)
|
|
":END:")
|
|
"\n")))
|
|
|
|
(add-to-list 'org-capture-templates
|
|
'("b" "Blog post" entry
|
|
(file mmk2410/blog-posts-file)
|
|
(function org-hugo-new-subtree-post-capture-template)
|
|
:prepend t :empty-lines 1
|
|
:immediate-finish t :jump-to-captured t)))
|
|
|
|
#+end_src
|
|
|
|
** org-tree-slide for presentations
|
|
|
|
A presentation tool for org-mode based on the visibility of outline trees
|
|
|
|
GitHub: [[https://github.com/takaxp/org-tree-slide][GitHub:takaxp/org-tree-slide]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/presentation-setup ()
|
|
(org-display-inline-images) ;; Can also use org-startup-with-inline-images
|
|
(setq-local face-remapping-alist '((default (:height 1.5) variable-pitch)
|
|
(header-line (:height 4.0) variable-pitch)
|
|
(org-document-title (:height 1.75) org-document-title)
|
|
(org-code (:height 1.55) org-code)
|
|
(org-table (:height 1.55) org-table)
|
|
(org-verbatim (:height 1.55) org-verbatim)
|
|
(org-block (:height 1.25) org-block)
|
|
(org-block-begin-line (:height 0.7) org-block)))
|
|
(setq header-line-format " ")
|
|
(setq visual-fill-column-width 150)
|
|
(hl-line-mode 0))
|
|
|
|
(defun mmk2410/presentation-end ()
|
|
(org-remove-inline-images)
|
|
(setq-local face-remapping-alist '((default variable-pitch default)))
|
|
(setq header-line-format nil)
|
|
(setq visual-fill-column-width 100)
|
|
(hl-line-mode 1))
|
|
|
|
(use-package org-tree-slide
|
|
:hook ((org-tree-slide-play . mmk2410/presentation-setup)
|
|
(org-tree-slide-stop . mmk2410/presentation-end))
|
|
:custom
|
|
(org-tree-slide-slide-in-effect t)
|
|
(org-tree-slide-activate-message "Presentation started!")
|
|
(org-tree-slide-deactivate-message "Presentation finished!")
|
|
(org-tree-slide-header t)
|
|
(org-tree-slide-breadcrumbs " > ")
|
|
(org-image-actual-width nil))
|
|
#+end_src
|
|
|
|
|
|
* Org Research Assistant
|
|
** Org Roam
|
|
|
|
Roam Research / Zettelkasten system using org-mode.
|
|
|
|
Source: [[https://www.orgroam.com][orgroam.com]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-roam
|
|
:commands (org-roam-node-insert org-roam-node-find org-roam-capture org-roam-buffer-toggle org-roam)
|
|
:config
|
|
(setq org-roam-directory "~/org/roam")
|
|
(add-to-list 'display-buffer-alist
|
|
'("\\*org-roam\\*"
|
|
(display-buffer-in-direction)
|
|
(direction . right)
|
|
(window-width . 0.33)
|
|
(window-height . fit-window-to-buffer)))
|
|
(setq org-roam-dailies-directory "daily/")
|
|
(setq org-roam-dailies-capture-templates
|
|
'(("c" "clock" entry "* %<%H:%M> %?"
|
|
:if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))
|
|
("n" "note" entry "* %?"
|
|
:if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))
|
|
("t" "todo" entry "* TODO %?"
|
|
:if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
|
|
(require 'org-roam-protocol)
|
|
(require 'org-roam-dailies)
|
|
(org-roam-db-autosync-mode)
|
|
:bind (("C-c r f" . org-roam-node-find)
|
|
("C-c r i" . org-roam-node-insert)
|
|
("C-c r b" . org-roam-buffer-toggle)
|
|
("C-c r c" . org-roam-capture)
|
|
("C-c r g" . org-roam-graph)
|
|
:map org-mode-map
|
|
("C-M-i" . completion-at-point)
|
|
:map org-roam-mode-map
|
|
("[mouse-1]" . #'org-roam-visit-thing)
|
|
:map org-roam-dailies-map
|
|
("Y" . org-roam-dailies-capture-yesterday)
|
|
("T" . org-roam-dailies-capture-tomorrow))
|
|
:bind-keymap ("C-c r d" . org-roam-dailies-map))
|
|
#+end_src
|
|
|
|
** Org Roam Bibtex
|
|
|
|
„Official“ connector between org-roam, BibTeX and org-ref.
|
|
|
|
Source: [[https://github.com/org-roam/org-roam-bibtex][GitHub: org-roam/org-roam-bibtex]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-roam-bibtex
|
|
:after org-roam
|
|
:hook (org-roam-mode . org-roam-bibtex-mode))
|
|
#+end_src
|
|
|
|
** Org Roam UI
|
|
|
|
A graphical frontend for exploring your org-roam Zettelkasten
|
|
|
|
Source: [[https://github.com/org-roam/org-roam-ui][GitHub: org-roam/org-roam-ui]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package websocket
|
|
:after org-roam)
|
|
|
|
(use-package org-roam-ui
|
|
:after (org-roam websocket)
|
|
:config
|
|
(setq org-roam-ui-sync-theme t
|
|
org-roam-ui-follow t
|
|
org-roam-ui-update-on-save t
|
|
org-roam-ui-open-on-start t))
|
|
#+end_src
|
|
|
|
** Helm Bibtex
|
|
|
|
Search and manage bibliographies in Emacs.
|
|
|
|
Source: [[https://github.com/tmalsburg/helm-bibtex][GitHub: tmalsburg/helm-bibtex]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ivy-bibtex
|
|
:after ivy
|
|
:config
|
|
(setq bibtex-completion-bibliography "~/org/roam/bibliography.bib")
|
|
(setq bibtex-completion-library-path "~/papers")
|
|
(setq bibtex-completion-notes-path "~/org/roam")
|
|
(setq bibtex-completion-find-additional-pdfs t))
|
|
#+end_src
|
|
|
|
** Org Ref
|
|
|
|
org-mode modules for citations, cross-references, bibliographies in org-mode and useful bibtex tools to go with it.
|
|
|
|
Source: [[https://github.com/jkitchin/org-ref][Github: jkitchin/org-ref]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-ref
|
|
:after org
|
|
:config
|
|
(setq org-ref-default-bibliography '("~/org/roam/bibliography.bib"))
|
|
(setq org-ref-pdf-directory "~/papers/")
|
|
(setq org-ref-notes-directory "~/org/roam")
|
|
(setq reftex-default-bibliography org-ref-default-bibliography)
|
|
(setq bibtex-completion-bibliography org-ref-default-bibliography)
|
|
(setq bibtex-completion-library-path org-ref-pdf-directory)
|
|
(setq bibtex-completion-notes-path org-ref-notes-directory))
|
|
#+end_src
|
|
|
|
** Org Noter
|
|
|
|
A synchronized, Org-mode, document annotator
|
|
|
|
Source: [[https://github.com/weirdNox/org-noter][GitHub: weirdNorx/org-noter]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-noter
|
|
:after (:any org pdf-tools)
|
|
:config
|
|
(setq org-noter-notes-search-path '("~/org/roam")))
|
|
#+end_src
|
|
|
|
* LaTeX configuration
|
|
** auctex
|
|
|
|
For LaTeX typesetting in Emacs I like to use the great AUCTeX package.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package auctex
|
|
:defer t
|
|
:config
|
|
(setq TeX-auto-save t)
|
|
(setq TeX-parse-self t)
|
|
(setq TeX-engine 'luatex)C
|
|
(setq-default TeX-master nil))
|
|
#+end_src
|
|
|
|
** company-auctex
|
|
|
|
I also like to turn on company completion for auctex.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package company-auctex
|
|
:after auctex
|
|
:config
|
|
(company-auctex-init))
|
|
#+end_src
|
|
|
|
** cdlatex
|
|
|
|
Fast input methods to enter LaTeX environments and math.
|
|
|
|
Source: [[https://github.com/cdominik/cdlatex][GitHub: cdominik/cdlatex]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package cdlatex
|
|
:diminish 'org-cdlatex-mode
|
|
:hook ((LaTeX-mode . turn-on-cdlatex)
|
|
(org-mode . turn-on-org-cdlatex)))
|
|
#+end_src
|
|
|
|
* General Programming Packages
|
|
** rainbow-delimiters
|
|
|
|
Lisp languages involve many parentheses and it might get hard to know which one are on the same “level”. The rainbow-delimiters package is here to help with this problem.
|
|
|
|
Source: [[https://github.com/Fanael/rainbow-delimiters][GitHub: Fanael/rainbow-delimiters]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package rainbow-delimiters
|
|
:hook (prog-mode . rainbow-delimiters-mode))
|
|
#+end_src
|
|
|
|
** highlight-indent-guides
|
|
|
|
Minor mode for highlighting indentation.
|
|
|
|
Source: [[https://github.com/DarthFennec/highlight-indent-guides][DarthFennec/highlight-indent-guides]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package highlight-indent-guides
|
|
:config
|
|
(setq highlight-indent-guides-method 'character
|
|
highlight-indent-guides-responsive 'top)
|
|
:hook (prog-mode . highlight-indent-guides-mode))
|
|
#+end_src
|
|
|
|
** company
|
|
|
|
Auto completion
|
|
|
|
Source: [[https://github.com/company-mode/company-mode][GitHub: company-mode]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package company
|
|
:diminish company-mode
|
|
:hook ((prog-mode . company-mode)
|
|
(latex-mode . company-mode))
|
|
:init
|
|
(setq company-show-numbers t
|
|
company-minimum-prefix-length 1
|
|
company-idle-delay 0.0
|
|
company-dabbrev-downcase nil))
|
|
#+end_src
|
|
|
|
** flycheck
|
|
|
|
Flycheck is a syntax checking extension for Emacs which uses specialized command line tools for the languages it supports. Next to the linting and highlighting of errors it does on its own it also integrates nicely into lsp-mode.
|
|
|
|
Source: [[https://www.flycheck.org][flycheck.org]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package flycheck
|
|
:diminish 'flycheck-mode
|
|
:config (setq-default flycheck-emacs-lisp-load-path 'inherit)
|
|
:init (global-flycheck-mode))
|
|
#+end_src
|
|
|
|
** eglot
|
|
|
|
A client for Language Server Protocol servers
|
|
|
|
Source: [[https://github.com/joaotavora/eglot][GitHub:joaotavora/eglot]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package eglot
|
|
:hook ((python-mode . eglot-ensure)
|
|
(go-mode . eglot-ensure)
|
|
(php-mode . eglot-ensure)
|
|
(vue-mode . eglot-ensure)
|
|
(web-mode . eglot-ensure)
|
|
(typescript-mode . eglot-ensure)
|
|
(js-mode . eglot-ensure))
|
|
:commands (eglot eglot-ensure))
|
|
#+end_src
|
|
|
|
** eldoc-box
|
|
|
|
childframe doc for eglot and anything that uses eldoc.
|
|
|
|
Source: [[https://github.com/casouri/eldoc-box][GitHub:casouri/eldoc-box]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package eldoc-box
|
|
:after eglot
|
|
:hook ((eglot-managed-mode . eldoc-box-hover-at-point-mode)))
|
|
#+end_src
|
|
|
|
** editorconfig
|
|
|
|
EditorConfig plugin for Emacs
|
|
|
|
Source: [[https://github.com/editorconfig/editorconfig-emacs][GitHub: editorconfig/editorconfig-emacs]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package editorconfig
|
|
:ensure t
|
|
:diminish 'editorconfig-mode
|
|
:config
|
|
(editorconfig-mode 1))
|
|
#+end_src
|
|
|
|
** Clojure
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package cider
|
|
:config
|
|
(setq cider-lein-parameters "repl :headless :host 0.0.0.0"))
|
|
#+end_src
|
|
|
|
** Docker
|
|
|
|
I use Docker and more importantly docker-compose for development. Thanks to [[https://github.com/Silex][Philippe Vaucher]] I can manage it from within Emacs.
|
|
|
|
- Source :: [[https://github.com/Silex/docker.el][GitHub: Silex/docker.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package docker
|
|
:bind ("C-c M-d" . docker))
|
|
#+end_src
|
|
|
|
* Language-Specific Programming Packages
|
|
** Go
|
|
|
|
Emacs mode for the Go programming language
|
|
|
|
Source: [[https://github.com/dominikh/go-mode.el][dominikh/go-mode.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package go-mode
|
|
:mode "\\.go\\'")
|
|
#+end_src
|
|
|
|
** PHP
|
|
|
|
PHP major mode for Emacs.
|
|
|
|
Source: [[https://github.com/emacs-php/php-mode][GitHub: emacs-php/php-mode]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package php-mode
|
|
:mode "\\.php\\'"
|
|
:config (setq php-mode-coding-style 'PSR2))
|
|
#+end_src
|
|
|
|
** TypeScript
|
|
|
|
TypeScript-support for Emacs
|
|
|
|
Source: [[https://github.com/emacs-typescript/typescript.el][GitHub: emacs-typescript/typescript.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package typescript-mode
|
|
:mode "\\.ts\\'"
|
|
:config
|
|
(setq typescript-indent-level 2))
|
|
#+end_src
|
|
|
|
** JavaScript
|
|
|
|
Load the built-in JavaScript mode to set it's indentation.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package js
|
|
:config
|
|
(setq js-indent-level 2))
|
|
#+end_src
|
|
|
|
** Web Mode
|
|
|
|
web-mode.el is an autonomous emacs major-mode for editing web templates.
|
|
|
|
Source: [[https://github.com/fxbois/web-mode][GitHub: fxbois/web-mode]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package web-mode
|
|
:mode (("\\.phtml\\'" . web-mode)
|
|
("\\.tpl\\.php\\'" . web-mode)
|
|
("\\.[agj]sp\\'" . web-mode)
|
|
("\\.as[cp]x\\'" . web-mode)
|
|
("\\.erb\\'" . web-mode)
|
|
("\\.mustache\\'" . web-mode)
|
|
("\\.djhtml\\'" . web-mode)
|
|
("\\.vue\\'" . web-mode)
|
|
("\\.blade\\.php\\'" . web-mode))
|
|
:config
|
|
(setq web-mode-script-padding 0)
|
|
(setq web-mode-code-indent-offset 2)
|
|
(setq web-mode-markup-indent-offset 2)
|
|
(setq web-mode-css-indent-offset 2)
|
|
(setq web-mode-style-padding 0)
|
|
(setq web-mode-enable-comment-annotation t))
|
|
#+end_src
|
|
|
|
** YAML
|
|
|
|
YAML major mode for Emacs.
|
|
|
|
Source: [[https://github.com/yoshiki/yaml-mode][GitHub: yoshiki/yaml-mode]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yaml-mode
|
|
:mode (("\\.yaml\\'" . yaml-mode)
|
|
("\\.yml\\'" . yaml-mode)))
|
|
#+end_src
|
|
|
|
** TypoScript
|
|
|
|
TypoScript major mode for Emacs.
|
|
|
|
Original Source: [[https://www.emacswiki.org/emacs/ts-mode.el][EmacsWiki: ts-mode.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ts-mode
|
|
:load-path "packags/"
|
|
:mode (("\\.typoscript\\'" . ts-mode)
|
|
("\\.tsconfig\\'" . ts-mode)))
|
|
#+end_src
|
|
|
|
** Markdown
|
|
|
|
Markdown mode customizations.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package markdown-mode
|
|
:mode "\\.md\\'"
|
|
:hook ((markdown-mode . visual-line-mode)
|
|
(markdown-mode . (lambda () (display-line-numbers-mode -1)))
|
|
(markdown-mode . variable-pitch-mode)
|
|
(markdown-mode . visual-fill-column-mode)))
|
|
#+end_src
|
|
|
|
* Emacs Multimedia System (EMMS)
|
|
|
|
For notes, tasks, writing, and countless other things there is org-mode.
|
|
|
|
For professional typesetting with TeX and LaTeX there is AucTeX.
|
|
|
|
And for playing music in Emacs there is EMMS.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package emms
|
|
:init
|
|
(emms-all)
|
|
(emms-default-players)
|
|
:config
|
|
(setq emms-source-file-default-directory "~/Music"))
|
|
#+end_src
|
|
|
|
* ERC (the Emacs IRC client)
|
|
|
|
** The beast itself
|
|
|
|
For joining IRC channels I use ERC. For connecting with the network (using my ZNC bouncer) I would like to just press a keybinding. Therefore I warp the call to =erc-tls= with the correct arguments in the following function (which is later bound to =C-c i j=).
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/erc-connect ()
|
|
"Start a IRC session with ERC by connecting to my ZNC bouncer."
|
|
(interactive)
|
|
(erc-tls :server "znc.mmk2410.org" :port "6697" :nick "mmk2410"
|
|
:full-name "Marcel Kapfer"))
|
|
#+end_src
|
|
|
|
Now the configuration for ERC. Some stuff I found in the deepest depths of the internet. Worth mentioning is the [[https://www.emacswiki.org/emacs/ERC][EmacsWiki]] and the following two videos by David Wilson on the [[https://www.youtube.com/c/SystemCrafters][System Crafters YouTube channel]]: [[https://www.youtube.com/watch?v=qWHTZIYTA4s][Chat Like the 90's in Emacs with ERC]] and [[https://www.youtube.com/watch?v=Qci8t_jpVGA][System Crafters Live! - Improving the IRC Experience in ERC]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package erc
|
|
:config
|
|
;; Display styling
|
|
(setq erc-fill-column 100
|
|
erc-fill-function 'erc-fill-static
|
|
erc-fill-static-center 20)
|
|
;; Emacs behavior
|
|
(setq erc-rename-buffers t
|
|
erc-kill-buffer-on-part t
|
|
erc-kill-queries-on-quit t
|
|
erc-kill-server-buffer-on-quit t)
|
|
;; IRC behavior
|
|
(setq erc-hide-list '("JOIN" "PART" "QUIT" "NICK" "MODE" "AWAY")
|
|
erc-join-buffer 'bury
|
|
erc-autojoin-timing 'ident
|
|
erc-track-exclude '("#guix")
|
|
erc-track-exclude-types '("JOIN" "NICK" "QUIT" "MODE" "AWAY" "PART")
|
|
erc-track-exclude-server-buffer t)
|
|
:bind (
|
|
("C-c i c" . 'mmk2410/erc-connect)
|
|
:map erc-mode-map
|
|
("RET" . nil)
|
|
("C-c <C-return>" . 'erc-send-current-line)))
|
|
#+end_src
|
|
|
|
** Additional packages
|
|
|
|
Highlight nicknames in ERC.
|
|
|
|
Source: [[https://github.com/leathekd/erc-hl-nicks][leathekd/erc-hl-nicks]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package erc-hl-nicks
|
|
:after erc
|
|
:config
|
|
(add-to-list 'erc-modules 'hl-nicks))
|
|
#+end_src
|
|
|
|
Fetch and show received images in a ERC buffer
|
|
|
|
Source: [[https://github.com/kidd/erc-image.el][kidd/erc-image.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package erc-image
|
|
:after erc
|
|
:config
|
|
(setq erc-image-inline-rescale 600)
|
|
(add-to-list 'erc-modules 'image))
|
|
#+end_src
|
|
|
|
* Emacs Web Wowser (EWW)
|
|
|
|
Normally I use Firefox for browsing the web, but when I am working with full focus in Emacs I sometimes prefer eww. For example while reading a paper and taking notes on it. In this case I don't want to lose my focus while switching to a web browser (and potentially see some open tabs from another project).
|
|
|
|
In recent times I have gotten really bad search results on DuckDuckGo (either I can't search anymore or they really got worse). So I'm using Startpage at the moment.
|
|
|
|
More important for me while reading a paper is looking up some things on Wikipedia. So I defined -- additionally to a simple keybinding for eww itself -- a function and a keybinding for searching on the English Wikipedia.
|
|
|
|
#+begin_src emacs-lisp
|
|
(eval-and-compile
|
|
(defun eww-browse-wikipedia-en ()
|
|
(interactive)
|
|
(let ((search (read-from-minibuffer "Wikipedia (EN) search: ")))
|
|
(eww-browse-url
|
|
(concat "https://en.wikipedia.org/w/index.php?search=" search)))))
|
|
|
|
(eval-and-compile
|
|
(defun eww-browser-english-dict ()
|
|
(interactive)
|
|
(let ((search (read-from-minibuffer "Dictionary (EN) search: ")))
|
|
(eww-browse-url
|
|
(concat "https://www.merriam-webster.com/dictionary/" search)))))
|
|
|
|
(use-package eww
|
|
:config
|
|
(setq eww-search-prefix "https://startpage.com/search/?q=")
|
|
:bind (("C-c w b" . 'eww)
|
|
("C-c w d" . 'eww-browser-english-dict)
|
|
("C-c w w" . 'eww-browse-wikipedia-en)))
|
|
#+end_src
|
|
|
|
* Terminals & Shells
|
|
** vterm
|
|
|
|
A fully-fledged terminal emulator based on libvterm.
|
|
|
|
Source: [[https://github.com/akermu/emacs-libvterm][GitHub: akermu/emacs-libvterm]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package vterm
|
|
:if (not (eq system-type 'windows-nt))
|
|
:config
|
|
(when on-macos
|
|
(setq vterm-shell "/opt/homebrew/bin/fish"))
|
|
:bind (("C-c t" . 'vterm)))
|
|
#+end_src
|
|
|
|
** eshell
|
|
|
|
Let's try out eshell.
|
|
|
|
Resources:
|
|
- [[https://www.masteringemacs.org/article/complete-guide-mastering-eshell][Mikey Petersen: Mastering Eshell]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package eshell
|
|
:bind (("C-c s" . 'eshell))
|
|
:config
|
|
(setq eshell-visual-subcommands
|
|
'(("docker" "pull" "build")
|
|
("docker-compose" "build"))))
|
|
#+end_src
|
|
|
|
* Elfeed
|
|
|
|
Since now I used many different RSS feed solutions. I started with Feedly back in 2013/14, switched later to a self-hosted TinyTinyRSS and then to Nextcloud News. After some time using this solution, its many bugs annoyed me more and more and I finally switched to FreshRSS in early 2021. Since early October I'm trying Miniflux. Although I had no problems with FreshRSS the interface of Miniflux (especially on mobile) is IMO nicer (though a little bit more eye-candy would be nice).
|
|
|
|
Mostly I use Miniflux through its web app---on mobile and on the desktop as well. But I have used and liked elfeed in the past and so I keep it configured properly.
|
|
|
|
** elfeed package
|
|
|
|
This is the elfeed program itself.
|
|
|
|
As with email I prefer showing older entries first so I can catch-up with these first before getting to newer stuff. Other than the binding to =C-c e= there is nothing interesting left to see here.
|
|
|
|
Source: [[https://github.com/skeeto/elfeed][GitHub: skeeto/elfeed]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package elfeed
|
|
:demand t
|
|
:config (setq elfeed-sort-order 'ascending)
|
|
:bind (("C-c e" . elfeed)))
|
|
#+end_src
|
|
|
|
** Storing articles
|
|
|
|
*Currently I am not using this since I need the actual articles offline for reading in some situation and therefore currently rely on Pocket.*
|
|
|
|
I like to call myself quite an avid org-mode user and so I also want to store RSS articles I read with Elfeed in a reading list. Since from time to time I prefer to use my tablet or smartphone to read instead of sitting at the computer and it is a little bit cumbersome to use Emacs with Elfeed using Termux I prefer using the fantastic Orgzly app. The problem is now that I cannot just =org-store-link= in the org-capture template for Elfeed entries since this would store a link to the corresponding Elfeed entry. While I can open such links at my desktop I cannot in Orgzly. And also on desktop I most likely will open the article in my web browser instead of viewing just the RSS entry. Therefore it would be very practical if the capture template would not store a Elfeed link but directly the web link. Additionally would also like to store the Feed title as part of the note.
|
|
|
|
Therefore I need a org-capture template which fetches these functions. And since I can only get the Elfeed entry properties in the buffer with the entry I cannot just use the =%(EXP)= option alone but need a function that switches to the Elfeed buffer, gets the property and then inserts the value in the capture template. For this I wrote the following function which does exactly this (thanks to [[https://emacs.stackexchange.com/a/55268][RichieHH]] who put me on the right track).
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/org-capture-elfeed-entry-props (prop-function)
|
|
(save-window-excursion
|
|
(switch-to-buffer (org-capture-get :original-buffer))
|
|
(funcall prop-function elfeed-show-entry)))
|
|
#+end_src
|
|
|
|
Using this function I can now define a org-capture template for my purpose.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'org-capture-templates
|
|
'("a" "RSS Article" entry (file "~/org/tasks/read-review.org")
|
|
"* TODO %(elfeed-feed-title (mmk2410/org-capture-elfeed-entry-props 'elfeed-entry-feed)): [[%(mmk2410/org-capture-elfeed-entry-props 'elfeed-entry-link)][%(mmk2410/org-capture-elfeed-entry-props 'elfeed-entry-title)]]\n :PROPERTIES:\n :CREATED: %U\n :END:\n"
|
|
:immediate-finish t))
|
|
#+end_src
|
|
|
|
** elfeed-protocol
|
|
|
|
=elfeed-protocol= is a addition to Elfeed which enables the usage of some cloud RSS services (e.g. Nextcloud News, TT-RSS).
|
|
|
|
I currently use it in combination with my Miniflux instance (previously I used it with Nextcloud News and FreshRSS). The reason why I not only use elfeed with a local feed list (managed for example by elfeed-org) is that a cloud service can check for new feed articles continuously and I can read articles on any device.
|
|
|
|
I additionally bind =elfeed-protocol-fever-reinit= to a handy keybinding since this seems to be the only way to retrieve new items from the API.
|
|
|
|
Source: [[https://github.com/fasheng/elfeed-protocol][GitHub: fasheng/elfeed-protocol]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package elfeed-protocol
|
|
:init
|
|
;; FreshRSS (self-hosted)
|
|
(setq elfeed-feeds '(("fever+https://mmk2410@rss.mmk2410.org"
|
|
:api-url "https://rss.mmk2410.org/api/fever.php"
|
|
:use-authinfo t)))
|
|
(elfeed-protocol-enable)
|
|
:bind (:map elfeed-search-mode-map
|
|
("C-c C-u" . elfeed-protocol-fever-reinit)))
|
|
#+end_src
|
|
|
|
* Mastodon
|
|
|
|
Emacs client for Mastodon.
|
|
|
|
[[https://codeberg.org/martianh/mastodon.el][Codeberg: martianh/mastodon.el]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mastodon
|
|
:config
|
|
(setq mastodon-instance-url "https://fosstodon.org"
|
|
mastodon-active-user "mmk2410"))
|
|
#+end_src
|
|
|
|
* mu4e
|
|
|
|
Managing e-mails is maybe one of the most critical tasks in my life. Over the last years I tried countless different clients, including the famous (neo)mutt, KMail, and Thunderbird. But it seems that I always go back to one specific: mu4e. Maybe because it is integrated in Emacs and Org-mode, maybe because I like using a keyboard-focused client and that's where I end. I don't now. But what I know is: I enjoy using it!
|
|
|
|
Resources:
|
|
- [[https://sites.uw.edu/bxf4/2022/09/01/getting-uw-outlook-365-oauth2-to-work-with-emacs-mu4e-mbsync-and-msmtp/][Getting UW Outlook 365 OAUTH2 to work with emacs, mu4e, mbsync, and msmtp]]
|
|
- [[https://github.com/harishkrupo/oauth2ms][harishkrupo/oauth2ms]]
|
|
|
|
** Load mu4e
|
|
|
|
Not all systems that I use have mu/mu4e installed. Either because I cannot really use it at work or because I'm running a native Windows instance or because I didn't completely setup the machine. Therefore I only load/execute the complete mu4e configuration if Emacs can find a =mu= executable.
|
|
|
|
#+begin_src emacs-lisp
|
|
(when (executable-find "mu") ;; Closed after all mu4e configuration
|
|
#+end_src
|
|
|
|
Not all distributions that I use have a enough up-to-date package of mu/maildir-utils in their repositories. Therefore on some system I compile and install it myself which ends in =/usr/local= instead of just =/usr.= Therefore I have a small function that finds the correct load-path for the mu4e package.
|
|
|
|
#+begin_src emacs-lisp
|
|
(eval-and-compile
|
|
(defun mmk2410/mu4e-load-path ()
|
|
(let* ((mu4e-site-lisp-path "/share/emacs/site-lisp/mu4e")
|
|
(mu4e-load-path-usr (concat "/usr" mu4e-site-lisp-path))
|
|
(mu4e-load-path-usr-local (concat "/usr/local" mu4e-site-lisp-path))
|
|
(mu4e-load-path-nix (concat (getenv "HOME") "/.nix-profile" mu4e-site-lisp-path))
|
|
(mu4e-load-path-debian "/usr/share/emacs/site-lisp/elpa")
|
|
(mu4e-load-path-macos "/opt/homebrew/share/emacs/site-lisp/mu/mu4e/"))
|
|
(cond ((file-directory-p mu4e-load-path-nix)
|
|
mu4e-load-path-nix)
|
|
((file-directory-p mu4e-load-path-usr-local)
|
|
mu4e-load-path-usr-local)
|
|
((file-directory-p mu4e-load-path-usr)
|
|
mu4e-load-path-usr)
|
|
((file-directory-p mu4e-load-path-debian)
|
|
mu4e-load-path-debian)
|
|
((file-directory-p mu4e-load-path-macos)
|
|
mu4e-load-path-macos)
|
|
(t (error "mu4e not found!"))))))
|
|
#+end_src
|
|
|
|
Using the determined load path I now load mu4e itself and bind it to a handy keybinding. Since I don't close the parenthesis of the ~use-package~ everything that follows this source code blocks is part of the config section.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mu4e
|
|
:load-path (lambda () (list (mmk2410/mu4e-load-path)))
|
|
:demand t
|
|
:bind ("C-c m" . mu4e)
|
|
:config ;; final closing parens at th end of the file
|
|
#+end_src
|
|
|
|
Once it is loaded I'd like to set mu4e as my default mail program in Emacs.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mail-user-agent 'mu4e-user-agent)
|
|
#+end_src
|
|
|
|
** Fetching new mail
|
|
|
|
I use mbsync for synchronising my messages and prefer that I can trigger the refresh from mu4e. Normally I use a [[https://git.mmk2410.org/mmk2410/dotfiles/src/branch/main/systemd][SystemD user service and timer]] for running mbsync every five minutes.
|
|
|
|
Apparently mbsync requires a filename change, when moving mails.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-get-mail-command "mbsync -a"
|
|
mu4e-change-filenames-when-moving t)
|
|
#+end_src
|
|
|
|
** Header view customizations
|
|
|
|
I'd like to use the IS-8601 date format.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-headers-date-format "%Y-%m-%d %H:%M")
|
|
#+end_src
|
|
|
|
For settings my header fields, I want to define a custom header field, which shows just an excerpt of my maildir path. My paths -- created by mbsync -- begin with the account name, which I want to shorten to just one letter. Since I archive my mail but let mu4e show me related mails, I want also to shorten the "Archive" path to "A". So the maildir path my be a short as e.g "m/INBOX" or "m/A".
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'mu4e-header-info-custom
|
|
'(:mdir .
|
|
( :name "Shortend Maildir path"
|
|
:shortname "Maildir"
|
|
:help "Shows a collapsed maildir path"
|
|
:function (lambda (msg)
|
|
(let ((maildir (or (mu4e-message-field msg :maildir) "")))
|
|
(cond ((string-match-p "Archives/" maildir)
|
|
(replace-regexp-in-string "^/\\(.\\).*/\\(.\\).*/\\(.*\\)" "\\1/\\2/\\3" maildir))
|
|
((string-match-p "Archives" maildir)
|
|
(replace-regexp-in-string "^/\\(.\\).*/\\(.\\).*" "\\1/\\2" maildir))
|
|
(t
|
|
(replace-regexp-in-string "^/\\(.\\).*/\\(.*\\)" "\\1/\\2" maildir))))))))
|
|
|
|
#+end_src
|
|
|
|
I want to use the IMO improved =:from-or-to= header field instead of only a =:from=. Next to that I prefer to show the subject in a thread only once for a cleaner overview.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-headers-fields
|
|
'((:human-date . 16)
|
|
(:flags . 4)
|
|
(:mdir . 12)
|
|
(:mailing-list . 10)
|
|
(:from-or-to . 22)
|
|
(:thread-subject)))
|
|
#+end_src
|
|
|
|
Additionally I prefer to change the sorting directory so that older messages are displayed on top. Since I try to keep an empty inbox and either reply to mails instantly or create a todo from them handling "old" mails should one the one hand be rare and on the other hand much more urgent. Actually I want them to distract and annoy me so that I handle them sooner than later.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-headers-sort-direction 'ascending)
|
|
#+end_src
|
|
|
|
As already said, I like to archive my mail and have as few mailboxes as possible. Since I get some mails over different addresses and in different mailboxes mu4e displays only one and doesn't show the duplicate. So when I mark the message I see for archiving and refresh the list while applying the mark the then hidden duplicate is now shown and I have to mark that again for archiving. Since the mailbox is shown I also can tell which one to archive if necessary. That is why I disable the skipping of duplicates:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-headers-skip-duplicates nil)
|
|
#+end_src
|
|
|
|
** Message view customizations
|
|
|
|
By default mu4e jumps to the next message when pressing =SPC= at the end of a message. I explicitly do not want that behavior.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-view-scroll-to-next nil)
|
|
#+end_src
|
|
|
|
I like to keep my Downloads folder quite empy and also download everything that I don't intent to keep to =/tmp=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-attachment-dir "/tmp")
|
|
#+end_src
|
|
|
|
The mu4e manual says, that viewing inline images may be not always correct. Let's try it anyway and find out how bad it really is. Additionally it is recommended, that =imagemagick-register-types= is called.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-view-show-images t)
|
|
(when (fboundp 'imagemagick-register-types)
|
|
(imagemagick-register-types))
|
|
#+end_src
|
|
|
|
I like to see the complete addresses and not just the name.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-view-show-addresses t)
|
|
#+end_src
|
|
|
|
** Compose messages
|
|
|
|
I prefer to BCC myself in mail so I now that a message was handled by the mail server and thereby I can also put all mails of a thread in the same folder which makes searching a bit easier. So I let mu4e automatically set the BCC header when I start to compose a message.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'mu4e-compose-mode-hook
|
|
(lambda ()
|
|
(save-excursion
|
|
(message-add-header (concat "Bcc: " user-mail-address "\n")))))
|
|
#+end_src
|
|
|
|
Since I get the emails via the BCC it is useless to also put them in a send folder.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-sent-messages-behavior 'delete)
|
|
#+end_src
|
|
|
|
Don't keep "zombie" buffers around after sending a mail.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq message-kill-buffer-on-exit t)
|
|
#+end_src
|
|
|
|
Also don't reply to myself in case a "reply to all".
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-compose-dont-reply-to-self t)
|
|
#+end_src
|
|
|
|
I'd like to send my messages as flowed format.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-compose-format-flowed t)
|
|
#+end_src
|
|
|
|
I like a "special" citation line...
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq message-citation-line-format "%f @ %Y-%m-%d %T %Z:\n"
|
|
message-citation-line-function 'message-insert-formatted-citation-line)
|
|
#+end_src
|
|
|
|
** Sending mail
|
|
|
|
Since I already have a msmtp client configured and would also use it in case I would use mutt, I want to use it also in mu4e. Setting msmtp as default SMTP client is quite easy but is it a little bit harder to set the account that should be used for sending. Therefore I wrote a lambda function that chooses the account and passes it as extra arguments based on the senders email address.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq message-send-mail-function 'message-send-mail-with-sendmail
|
|
sendmail-program "msmtp"
|
|
message-sendmail-envelope-from 'header)
|
|
(add-hook 'message-send-mail-hook
|
|
(lambda ()
|
|
(save-excursion
|
|
(let* ((from (save-restriction
|
|
(message-narrow-to-headers)
|
|
(message-fetch-field "from")))
|
|
(account (cond
|
|
((string-match "@uni-ulm.de" from) "alumni")
|
|
((string-match "@emplify-software.de" from) "work")
|
|
(t "mailbox"))))
|
|
(setq message-sendmail-extra-arguments (list '"-a" account))))))
|
|
#+end_src
|
|
|
|
** Define custom bookmarks
|
|
|
|
I don't really like the default bookmarks that mu4e sets. Thats why I overwrite the =mu4e-bookmarks= variable completely instead of just appending new bookmarks.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if (mmk2410/work)
|
|
(setq mu4e-bookmarks
|
|
'(( :name "Unread work messages"
|
|
:key ?w
|
|
:query "(flag:unread and m:/work/*) or m:/work/inbox" )
|
|
( :name "Unread no-reply work messages"
|
|
:key ?n
|
|
:query "flag:unread and m:/work-noreply/*" )
|
|
( :name "Unread private messages"
|
|
:key ?p
|
|
:query "(flag:unread and m:/mailbox/*) or m:/mailbox/inbox" )))
|
|
(setq mu4e-bookmarks
|
|
'(( :name "Unread messages"
|
|
:key ?u
|
|
:query "flag:unread or m:/alumni/inbox or m:/mailbox/inbox" )
|
|
( :name "Unread alumni messages"
|
|
:key ?a
|
|
:query "(flag:unread and m:/alumni/*) or m:/alumni/inbox" )
|
|
( :name "Unread private messages"
|
|
:key ?p
|
|
:query "(flag:unread and m:/mailbox/*) or m:/mailbox/inbox" ))))
|
|
#+end_src
|
|
|
|
** Define some shortcuts
|
|
|
|
I like to get to some maildir quite fast and with a shortcut.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if (mmk2410/work)
|
|
(setq mu4e-maildir-shortcuts
|
|
'((:maildir "/work/inbox" :key ?w)
|
|
(:maildir "/mailbox/inbox" :key ?p)))
|
|
(setq mu4e-maildir-shortcuts
|
|
'((:maildir "/alumni/inbox" :key ?a)
|
|
(:maildir "/mailbox/inbox" :key ?p))))
|
|
#+end_src
|
|
|
|
** Org-Capture template and key bindings
|
|
|
|
I use org-mode heavily and also manage my todos within it. Since my INBOX is not a todo list I like the idea of quickly add a mail as a todo.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if (mmk2410/work)
|
|
(add-to-list 'org-capture-templates
|
|
'("m" "mail" entry (file "~/org/tasks/inbox.org")
|
|
"* TODO %a%? :@work:\n:PROPERTIES:\n:CREATED:%U\n:END:\n"))
|
|
(add-to-list 'org-capture-templates
|
|
'("m" "mail" entry (file "~/org/tasks/inbox.org")
|
|
"* TODO %a%?\n :PROPERTIES:\n :CREATED: %U\n :END:\n")))
|
|
#+end_src
|
|
|
|
** mu4e-contexts
|
|
|
|
Multiple mail accounts in mu4e can be achieved by using so called contexts.
|
|
|
|
First of all I like mu4e to only ask me, which content to use, if it can't figure it you.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-context-policy 'ask
|
|
mu4e-compose-context-policy 'ask)
|
|
#+end_src
|
|
|
|
Now define the contexts.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if (mmk2410/work)
|
|
(setq mu4e-contexts
|
|
`( ,(make-mu4e-context
|
|
:name "Private"
|
|
:enter-func (lambda () (mu4e-message "Entering private context"))
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/mailbox" (mu4e-message-field msg :maildir))))
|
|
:vars '( ( user-mail-address . "me@mmk2410.org" )
|
|
( user-full-name . "Marcel Kapfer" )
|
|
( message-signature-file . nil)
|
|
( mu4e-sent-folder . "/mailbox/Sent" )
|
|
( mu4e-drafts-folder . "/mailbox/Drafts" )
|
|
( mu4e-trash-folder . "/mailbox/Trash" )
|
|
( mu4e-refile-folder . "/mailbox/Archives" )))
|
|
,(make-mu4e-context
|
|
:name "Work"
|
|
:enter-func (lambda () (mu4e-message "Entering work context"))
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/work" (mu4e-message-field msg :maildir))))
|
|
:vars '( ( user-mail-address . "m.kapfer@emplify-software.de" )
|
|
( user-full-name . "Marcel Kapfer" )
|
|
( message-signature-file . "~/dotfiles/dot-work/signature/mu4e")
|
|
( mu4e-sent-folder . "/work/Sent Items" )
|
|
( mu4e-drafts-folder . "/work/Drafts" )
|
|
( mu4e-trash-folder . "/work/Trash" )
|
|
( mu4e-refile-folder . "/work/Archive" )))))
|
|
(setq mu4e-contexts
|
|
`( ,(make-mu4e-context
|
|
:name "Private"
|
|
:enter-func (lambda () (mu4e-message "Entering Private context"))
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/mailbox" (mu4e-message-field msg :maildir))))
|
|
:vars '( ( user-mail-address . "me@mmk2410.org" )
|
|
( user-full-name . "Marcel Kapfer" )
|
|
( message-signature-file . nil)
|
|
( mu4e-sent-folder . "/mailbox/Sent" )
|
|
( mu4e-drafts-folder . "/mailbox/Drafts" )
|
|
( mu4e-trash-folder . "/mailbox/Trash" )
|
|
( mu4e-refile-folder . "/mailbox/Archives" )))
|
|
,(make-mu4e-context
|
|
:name "uulmhack"
|
|
:enter-func (lambda () (mu4e-message "Entering uulmhack context"))
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(or (string-prefix-p "/alumni/uulmhack" (mu4e-message-field msg :maildir))
|
|
(string-prefix-p "/alumni/Archives/uulmhack" (mu4e-message-field msg :maildir)))))
|
|
:vars '( ( user-mail-address . "uulmhack@uni-ulm.de" )
|
|
( user-full-name . "uulmhack Orga-Team" )
|
|
( message-signature-file . "~/.dotfiles/dot-private/sig/uulmhack")
|
|
( mu4e-sent-folder . "/alumni/Sent" )
|
|
( mu4e-drafts-folder . "/alumni/Drafts" )
|
|
( mu4e-trash-folder . "/alumni/Trash" )
|
|
( mu4e-refile-folder . "/alumni/Archives" )))
|
|
,(make-mu4e-context
|
|
:name "Alumni"
|
|
:enter-func (lambda () (mu4e-message "Entering alumni context"))
|
|
:match-func (lambda (msg)
|
|
(when msg
|
|
(string-prefix-p "/alumni" (mu4e-message-field msg :maildir))))
|
|
:vars '( ( user-mail-address . "marcel.kapfer@alumni.uni-ulm.de" )
|
|
( user-full-name . "Marcel Kapfer" )
|
|
( message-signature-file . nil)
|
|
( mu4e-sent-folder . "/alumni/Sent" )
|
|
( mu4e-drafts-folder . "/alumni/Drafts" )
|
|
( mu4e-trash-folder . "/alumni/Trash" )
|
|
( mu4e-refile-folder . "/alumni/Archives" ))))))
|
|
#+end_src
|
|
|
|
** Other settings
|
|
|
|
The [[https://github.com/org-mime/org-mime][org-mime]] package thankfully allows sending HTML mails from mu4e. Not that I like HTML mails but I need to have a special HTML message signature for work.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-mime
|
|
:after (org mu4e)
|
|
:config (setq org-mime-library 'mml))
|
|
#+end_src
|
|
|
|
Mu4e displays a message in the minibuffer after refreshing. And since I run mbsync and mu index (through emacsclient) automatically every few minutes these messages can get quite distracting. Luckily mu4e provides an option to disable these index messages.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-hide-index-messages t)
|
|
#+end_src
|
|
|
|
Mu4e show its current search in the modeline and as you may have already noticed, my default searches tend to be large. So I increase the shown characters from 30 to 60.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-modeline-max-width 60)
|
|
#+end_src
|
|
|
|
It is maybe not surprising that I am subscribed to some mailing lists. Some of these have quite some long names and so I would like to use a some alias for the display of these names. Thanks god mu4e also provides a possibility for this!
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-user-mailing-lists '(("debian-user.lists.debian.org" . "DebUsr")
|
|
("debian-security-announce.lists.debian.org" . "DebSecAnn")))
|
|
#+end_src
|
|
|
|
Finally the mu4e configuration is done! So lets close the parenthesis left open since the ~use-package~ call and the ~(when~ call.
|
|
|
|
#+begin_src emacs-lisp
|
|
) ;; closing parens for (use-package mu4e
|
|
) ;; closing parens for (when (executable-find "mu")
|
|
#+end_src
|
|
|
|
* Email with Evolution
|
|
|
|
Since my work place is using Microsoft Outlook for Email including its appointment features I sadly needed to switch away from mu4e to Evolution, as recommended by [[https://karl-voit.at/2021/06/01/Thunderbird-to-Evolution/][Karl Voit]]. Using [[https://github.com/novoid/dot-emacs/blob/master/config.org][his configuration]] as an inspiration and source I could setup the following workflow.
|
|
|
|
I first define the path to my evolution binary.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mmk2410/evolution-path "/usr/bin/evolution")
|
|
#+end_src
|
|
|
|
Then I copied and slightly adjusted Karl Voits function for handling =message-id= link in Org mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/open-message-id-in-evolution (message-id)
|
|
(interactive)
|
|
(start-process
|
|
(concat "mid: " message-id)
|
|
nil
|
|
mmk2410/evolution-path
|
|
(concat "mid:<" message-id ">")))
|
|
|
|
(org-link-set-parameters "messageid" :follow #'mmk2410/open-message-id-in-evolution)
|
|
#+end_src
|
|
|
|
The following function from him takes care of the other way: bringing a message as a link into Org mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun mmk2410/convert-mail-header-to-org-link ()
|
|
"Assumes an email header in the killring, parses it and returns an org mode link for it."
|
|
(interactive)
|
|
(with-temp-buffer
|
|
(save-match-data
|
|
(yank)
|
|
|
|
(goto-char (point-min))
|
|
(re-search-forward "^Message-Id:.+<\\(.+\\)>[ ]*$" nil nil 1)
|
|
(setq message-id (match-string 1))
|
|
|
|
(goto-char (point-min))
|
|
(re-search-forward "^From:[ ]+\\(.+?\\)[ ]*$" nil nil 1)
|
|
(setq from (match-string 1))
|
|
|
|
(goto-char (point-min))
|
|
(re-search-forward "^Subject:[ ]+\\(.+?\\)[ ]*$" nil nil 1)
|
|
(setq subject (match-string 1))
|
|
|
|
(goto-char (point-min))
|
|
(re-search-forward "^Date:[ ]+\\(.+?\\)[ ]*$" nil nil 1)
|
|
(setq rawdate (match-string 1))
|
|
(setq date
|
|
(let ((time (date-to-time rawdate)))
|
|
(set-time-zone-rule t)
|
|
(prog1 (format-time-string "%Y-%m-%d %H:%M" time)
|
|
(set-time-zone-rule nil))))))
|
|
(insert (concat "[[messageid:" message-id "][" date " " from ": " subject "]]")))
|
|
|
|
(global-set-key (kbd "C-c M-m") 'mmk2410/convert-mail-header-to-org-link)
|
|
#+end_src
|
|
|
|
* Helpers
|
|
** Tab Bar Setup
|
|
|
|
Since version 27 Emacs features a tab bar. In contrast to e.g. a browser a tab does not display just one file/buffer/window but an Emacs tab features an entire window configuration. Since I use Emacs not just for programming but (perhaps even mainly) for personal information management (including mail, agenda, journal, IRC and RSS) I had the idea to have one Emacs frame open with different tabs for all these things. Therefore I wrote a small package found in =packages/mmk2410-tab-bar-helpers.el= which I explain in a [[https://mmk2410.org/2022/02/11/using-emacs-tab-bar-mode/][dedicated blog post]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mmk2410-tab-bar-helpers
|
|
:after (hydra)
|
|
:load-path "packages/"
|
|
:bind ("C-c f" . mmk2410/tab-bar/body))
|
|
#+end_src
|
|
|
|
* Work
|
|
|
|
For my workplace I have some additional packages and configuration that I keep in an own file.
|
|
|
|
#+begin_src emacs-lisp
|
|
(if (mmk2410/work)
|
|
(org-babel-load-file
|
|
(expand-file-name "work/config.org" user-emacs-directory)))
|
|
#+end_src
|