From 56ae26078b6ac475a3a95c8d366a36b7ead37175 Mon Sep 17 00:00:00 2001 From: Swarsel Date: Thu, 20 Jun 2024 16:18:07 +0200 Subject: [PATCH] refactor: consolidate Nix.org and Emacs.org The configuration options that used to be stored in Emacs.org and Nix.org are now no longer split into two files. Instead, there now is a single file SwarselSystems.org that holds both these configs. --- .github/README.md | 8 +- Emacs.html | 4076 ----------- Emacs.org | 3243 -------- Nix.org => SwarselSystems.org | 5118 +++++++++++-- Wiki.org | 28 + flake.lock | 22 - flake.nix | 18 +- index.html | 12574 ++++++++++++++++++++++++++++++++ profiles/TEMPLATE/home.nix | 3 - profiles/TEMPLATE/nixos.nix | 1 - profiles/common/home.nix | 109 +- profiles/common/nixos.nix | 285 +- profiles/fourside/home.nix | 3 - profiles/fourside/nixos.nix | 1 - profiles/onett/home.nix | 3 - profiles/onett/nixos.nix | 1 - profiles/stand/home.nix | 3 - profiles/stand/nixos.nix | 1 - profiles/surface/home.nix | 1 - profiles/threed/home.nix | 3 - profiles/threed/nixos.nix | 1 - profiles/twoson/home.nix | 3 - profiles/twoson/nixos.nix | 1 - programs/emacs/init.el | 25 +- 24 files changed, 17122 insertions(+), 8409 deletions(-) delete mode 100644 Emacs.html delete mode 100644 Emacs.org rename Nix.org => SwarselSystems.org (53%) create mode 100644 index.html diff --git a/.github/README.md b/.github/README.md index 6547ca8..eb083b3 100644 --- a/.github/README.md +++ b/.github/README.md @@ -23,10 +23,10 @@ That being said, there is a lot of general configuration that you *probably* can The files that are possibly of biggest interest are found here: +- [SwarselSystems.org](../SwarselSystems.org) - [flake.nix](../flake.nix) -- [Nix.org](../Nix.org) - [early-init.el](../programs/emacs/early-init.el) -- [Emacs.org](../Emacs.org) +- [init](../programs/emacs/init.el) This is a nix flakes based setup that manages multiple hosts, including mixed (NixOS with home-manager as a submodule) as well as standalone home-manager machines, also using some overlays etc. There even is a configuration for an Android build. It is all wrapped in literal configuration .org files, because that allows me to have easy access without actually having to remember where the specific configuration files are all located. early-init.el is not tangled for the reason that adding it would break the emacs-overlay parsing. @@ -52,9 +52,9 @@ Below is a rough general guide to setup this system on a new NixOS host. **Again For a pure Home-Manager configuration, you need a few different steps. The biggest change is that you then want to call `home-manager --flake .#@ switch` as the last step instead of `nixos-rebuild [...]`. A complete general guide for that case cannot really be given since you are most likely setting up the flake on a existing machine that already has a lot of configuration. If you are setting up a new system, I would recommend to use NixOS unless circumstances force you to use something else. ###### To do that: -1) adapt [Nix.org](../Nix.org) +1) adapt [SwarselSystems.org](../SwarselSystems.org) 1) adapt system specific options: - - Make a copy of "System Specific Configurations/TEMPLATE". + - Make a copy of "System/System Specific Configurations/TEMPLATE". - Adapt all references to TEMPLATE to your host- and usernames etc - pay special attention to the header lines in each nix source block, i.e. the "#+begin_src nix [...] :tangle profiles/TEMPLATE/[...]" lines. - Add the settings needed for your specific machine. diff --git a/Emacs.html b/Emacs.html deleted file mode 100644 index 0a689c1..0000000 --- a/Emacs.html +++ /dev/null @@ -1,4076 +0,0 @@ - - - - - - - -Emacs Configuration - - - - - -
-

Emacs Configuration

-
-

Table of Contents

-
- -
-
-

-This file has 16683 words and was last revised on 2024-06-20 01:55:44 +0200. -

- -
-

1. Introduction (no code)

-
-

-This is my Emacs literate configuration. It is part of a NixOS system that is fully declarative and can be found here: -

- - - -

-The literate configuration lets me explain my choices to my future self as well as you, the reader. I go to great lengths to explain the choices for all configuration steps that I take in order for me to pay due diligence in crafting my setup, and not simply copying big chunks of other peoples code. Also, the literate configuration approach is very convenient to me as I only need to keep of (ideally) a single file to manage all of my configuration. I hope that this documentation will make it easier for beginners to get into Emacs and NixOS as I know it can be a struggle in the beginning. -

- -

-Due to a NixOS specific setting, I currently manage two files (Emacs.org and Nixos.org), but I hope to soon find a way to consolidate them into a single file. -

- -

-My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2024-06-20 01:55:44 +0200) -

- -
-
-system-configuration-options
-
-
-
- -
---prefix=/nix/store/lymgpfqr5dp1wc0khbcbhhjnxq8ccsy9-emacs-pgtk-20240521.0 --disable-build-details --with-modules --with-pgtk --with-compress-install --with-toolkit-scroll-bars --with-native-compilation --without-imagemagick --with-mailutils --without-small-ja-dic --with-tree-sitter --without-xinput2 --with-xwidgets --with-dbus --with-selinux
-
- - -

-This file is not loaded by Emacs directly as the configuration (even though this would be possible) - instead, it generates two more files: -

- -
    -
  • early-init.el -This file handle startup optimization and sets up the basic frame that I will be working in.
  • -
  • init.el -This file handles the rest of the Emacs configuration.
  • -
- -

-By using the configuration offered by this file, the file you are reading right now (Emacs.org) will be freshly tangled on every file save. However, when you clone this configuration yourself and have not yet activated it, you need to tangle the file yourself. This can be done using the keybind C-c C-v t. Alternatively, execute the following block: -

- -
-
-(org-babel-tangle)
-
-
-
- -

-Lastly, you will notice there is no package system setup in this configuration. This is because packages are automatically handled on the NixOS side by parsing the Emacs.org file for package installs. -

-
-
-
-

2. Initialization (early-init.el)

-
-

-In this section I handle my early init file; it takes care of frame-setup for emacsclient buffers. -

-
- -
-

2.1. Increase startup performance

-
-

-First, I use some advice from doomemacs regarding garbace collection; here I make sure that during startup, the garbace collectur will not run, which will improve startup times. Now, that might not really be needed since I will usually only start the emacs server once during startup and then not touch it again, however, since I am building my emacs configuration using NixOS, there is some merit to this since I will usually need to restart the server once I rebuild my configuration. -

- -

-Also, inspired by a setting I have seen in protesilaos' configuration, I apply the same idea to the file-name-handler-alist and vc-handled-backends. -

- -

-In the end, we need to restore those values to values that will work during normal operation. For that, I add a hook to the startup function that will revert the values once Emacs has finished initialization. -

- -

-Also packed into the hook function is the line (fset 'epg-wait-for-status 'ignore). This line is needed at the end of the configuration in order to allow for my Yubikey to be used to encrypt and decrypt .gpg files. Without it, Emacs will just hang forever and basically crash. -

- -
-
(defvar swarsel-file-name-handler-alist file-name-handler-alist)
-(defvar swarsel-vc-handled-backends vc-handled-backends)
-
-(setq gc-cons-threshold most-positive-fixnum
-      gc-cons-percentage 0.6
-      file-name-handler-alist nil
-      vc-handled-backends nil)
-
-(add-hook 'emacs-startup-hook
-          (lambda ()
-            (progn
-              (setq gc-cons-threshold (* 1000 1000 8)
-                    gc-cons-percentage 0.1
-                    file-name-handler-alist swarsel-file-name-handler-alist
-                    vc-handled-backends swarsel-vc-handled-backends)
-              (fset 'epg-wait-for-status 'ignore)
-              )))
-
-
-
-
-
-
-

2.2. Setup frames

-
-

-Next, I will setup the basic frame for my emacs buffers. Note that I use a tiling window manager, so I do not need to hold myself up with sizing the windows myself. I also disable some GUI tools that I (like many others) do not find to be particularly useful. Also I inhibit many startup functions here, even though it does not affect me greatly since I use another solution for that. -

- -

-We also make require immediate compilation of native code. -

- -

-For the default-frame-alist, I used to also set '(right-divider-width . 4) and '(bottom-divider-width . 4), but I did not like the look of the divider bar and usually know my splits anyways, so this is no longer set. -

- -
-
(tool-bar-mode 0)
-(menu-bar-mode 0)
-(scroll-bar-mode 0)
-
-(setq frame-inhibit-implied-resize t
-      ring-bell-function 'ignore
-      use-dialog-box nil
-      use-file-dialog nil
-      use-short-answers t
-      inhibit-startup-message t
-      inhibit-splash-screen t
-      inhibit-startup-screen t
-      inhibit-x-resources t
-      inhibit-startup-buffer-menu t
-      inhibit-startup-echo-area-message user-login-name ; this needs to be set to the username or it will not have an effect
-      comp-deferred-compilation nil ; compile all Elisp to native code immediately
-      )
-
-(setq-default left-margin-width 1
-              right-margin-width 1)
-
-(setq-default default-frame-alist
-      (append
-       (list
-        '(undecorated . t) ; no title bar, borders etc.
-        '(background-color . "#1D252C") ; load doom-citylight colors to avoid white flash
-        '(foreground-color . "#A0B3C5") ; load doom-citylight colors to avoid white flash
-        '(vertical-scroll-bars . nil)
-        '(horizontal-scroll-bars . nil)
-        '(internal-border-width . 5)
-        '(tool-bar-lines . 0)
-        '(menu-bar-lines . 0))))
-
-
-
-
-
-
-

2.3. Make C-i, C-m, C-[ available in graphic sessions

-
-

-By default, emacs binds -

-
    -
  • C-i to the TAB key
  • -
  • C-m to the RET key
  • -
  • C-[ to the ECS key
  • -
- -

-These keybinds exist to make Emacs work well in terminal mode. However, most of the time I am using Emacs in a graphic session, and I would hence like to have these keybinds available for personal use. -

- -

-NOTE: To use these keybinds, you need to enclose the binding in angled brackets (<>). Then they can be used normally -

- -
-
-(add-hook
-    'after-make-frame-functions
-    (lambda (frame)
-      (with-selected-frame frame
-        (when (display-graphic-p)
-          (define-key input-decode-map (kbd "C-i") [DUMMY-i])
-          (define-key input-decode-map (kbd "C-[") [DUMMY-lsb])
-          (define-key input-decode-map (kbd "C-m") [DUMMY-m])
-          ))))
-
-
-
-
-
-
-
-
-
-
-

3. Personal settings

-
-

-This section is used to define my own functions, own variables, and own keybindings. -

-
- -
-

3.1. Custom functions

-
-

-In this section I define extra functions that I need. Some of these functions I wrote myself, some I found after internet reseach. For functions I found on the internet, I will link the original source I found it in. -

-
- -
-

3.1.1. Emacs/Evil state toggle

-
-

-Since I am rebinding the C-z hotkey for emacs-evil-state toggling, I want to have a function that still lets me perform this action quickly. -

- -
-
-(defun swarsel/toggle-evil-state ()
-  (interactive)
-  (if (or (evil-emacs-state-p) (evil-insert-state-p))
-      (evil-normal-state)
-    (evil-emacs-state)))
-
-
-
-
-
- -
-

3.1.2. Switching to last used buffer

-
-

-I often find myself bouncing between two buffers when I do not want to use a window split. This funnction simply jumps to the last used buffer. -

- -
-
-(defun swarsel/last-buffer () (interactive) (switch-to-buffer nil))
-
-
-
-
-
-
-

3.1.3. mu4e functions

-
-

-I use these functions to let me switch between my main email accounts, as mu4e by itself has trouble doing so. mu4e-switch-account allows for manual choosing of the sender account, while mu4e-rfs--matching-address and mu4e-send-from-correct-address are used when replying to a mail; they switch the sender account to the one that received the mail. -

- -

-By default, the sender email will not be changed after sending a mail; however, I want Emacs to always use my main address when not replying to another email. For that I use mu4e-restore-default. -

- -

-Used here: mu4e -

- -
-
-(defun swarsel/mu4e-switch-account ()
-  (interactive)
-  (let ((account (completing-read "Select account: " mu4e-user-mail-address-list)))
-    (setq user-mail-address account)))
-
-(defun swarsel/mu4e-rfs--matching-address ()
-  (cl-loop for to-data in (mu4e-message-field mu4e-compose-parent-message :to)
-           for to-email = (pcase to-data
-                            (`(_ . email) email)
-                            (x (mu4e-contact-email x)))
-           for to-name =  (pcase to-data
-                            (`(_ . name) name)
-                            (x (mu4e-contact-name x)))
-           when (mu4e-user-mail-address-p to-email)
-           return (list to-name to-email)))
-
-(defun swarsel/mu4e-send-from-correct-address ()
-  (when mu4e-compose-parent-message
-    (save-excursion
-      (when-let ((dest (swarsel/mu4e-rfs--matching-address)))
-        (cl-destructuring-bind (from-user from-addr) dest
-          (setq user-mail-address from-addr)
-          (message-position-on-field "From")
-          (message-beginning-of-line)
-          (delete-region (point) (line-end-position))
-          (insert (format "%s <%s>" (or from-user user-full-name) from-addr)))))))
-
-(defun swarsel/mu4e-restore-default ()
-  (setq user-mail-address "leon@swarsel.win"
-        user-full-name "Leon Schwarzäugl"))
-
-
-
-
-
-
-
-

3.1.4. Create non-existant directories when finding file

-
-

-This function will check if a directory for which a file we want to open exists; if not, it will offer to create the directories for me. -

- -
-
-(defun swarsel/with-buffer-name-prompt-and-make-subdirs ()
-  (let ((parent-directory (file-name-directory buffer-file-name)))
-    (when (and (not (file-exists-p parent-directory))
-               (y-or-n-p (format "Directory `%s' does not exist! Create it? " parent-directory)))
-      (make-directory parent-directory t))))
-
-(add-to-list 'find-file-not-found-functions #'swarsel/with-buffer-name-prompt-and-make-subdirs)
-
-
-
-
-
-
-

3.1.5. [crux] Duplicate Lines

-
-

-When programming, I like to be able to duplicate a line. There are easier functions than the one below, but they either -

- -
    -
  1. screw with undo/redo
  2. -
  3. move the cursor wildly
  4. -
- -

-The below function avoids these problems. Originally I used the function duplicate-line found here: https://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs -

- -

-However, this function does not work on regions. Later, I found a solution implemented by crux. I do not need the whole package, so I just extracted the three functions I needed from it. -

- -
-
-(defun crux-get-positions-of-line-or-region ()
-  "Return positions (beg . end) of the current line or region."
-  (let (beg end)
-    (if (and mark-active (> (point) (mark)))
-        (exchange-point-and-mark))
-    (setq beg (line-beginning-position))
-    (if mark-active
-        (exchange-point-and-mark))
-    (setq end (line-end-position))
-    (cons beg end)))
-
-(defun crux-duplicate-current-line-or-region (arg)
-    "Duplicates the current line or region ARG times.
-  If there's no region, the current line will be duplicated.  However, if
-  there's a region, all lines that region covers will be duplicated."
-    (interactive "p")
-    (pcase-let* ((origin (point))
-                 (`(,beg . ,end) (crux-get-positions-of-line-or-region))
-                 (region (buffer-substring-no-properties beg end)))
-      (dotimes (_i arg)
-        (goto-char end)
-        (newline)
-        (insert region)
-        (setq end (point)))
-      (goto-char (+ origin (* (length region) arg) arg))))
-
-(defun crux-duplicate-and-comment-current-line-or-region (arg)
-  "Duplicates and comments the current line or region ARG times.
-If there's no region, the current line will be duplicated.  However, if
-there's a region, all lines that region covers will be duplicated."
-  (interactive "p")
-  (pcase-let* ((origin (point))
-               (`(,beg . ,end) (crux-get-positions-of-line-or-region))
-               (region (buffer-substring-no-properties beg end)))
-    (comment-or-uncomment-region beg end)
-    (setq end (line-end-position))
-    (dotimes (_ arg)
-      (goto-char end)
-      (newline)
-      (insert region)
-      (setq end (point)))
-    (goto-char (+ origin (* (length region) arg) arg))))
-
-
-
-
-
-
-

3.1.6. [prot] org-id-headings

-
-

-These functions by protesilaos generate heading links in an org-file similar to the normal org-store-link approach when not using properties. This approach has a weakness however - if the heading name is changed, the link breaks. These functions generate a unique identifier for each heading which will not break and also works when exporting the file to html, for example. -

- -
-
-(defun prot-org--id-get ()
-  "Get the CUSTOM_ID of the current entry.
-If the entry already has a CUSTOM_ID, return it as-is, else
-create a new one."
-  (let* ((pos (point))
-         (id (org-entry-get pos "CUSTOM_ID")))
-    (if (and id (stringp id) (string-match-p "\\S-" id))
-        id
-      (setq id (org-id-new "h"))
-      (org-entry-put pos "CUSTOM_ID" id)
-      id)))
-
-(declare-function org-map-entries "org")
-
-(defun prot-org-id-headlines ()
-  "Add missing CUSTOM_ID to all headlines in current file."
-  (interactive)
-  (org-map-entries
-   (lambda () (prot-org--id-get))))
-
-(defun prot-org-id-headline ()
-  "Add missing CUSTOM_ID to headline at point."
-  (interactive)
-  (prot-org--id-get))
-
-
-
-
-
-
-

3.1.7. Inhibit Messages in Echo Area

-
-

-Emacs likes to send messages to the echo area; this is generally a good thing. However, it bothers me a lot when I am currently working in minibuffer where I receive an echo area message that is actually important and it is then overwritten by e.g. the mu4e update message. This section makes it possible to find the root function calling the message function and disabling it here. -

- -

-Usage: Enable the (advice-add 'message :around #'who-called-me?) by running this code block, which will show a full trace of all messages being sent to the echo area: -

- -
-
-(advice-add 'message :around #'who-called-me?)
-
-
-
- -

-Once the root function has been found, it can be disabled via advice=add as in the last block in this section. To disable the stack tracing, run (advice-remove 'message #'who-called-me?) or the following code block: -

- -
-
-(advice-remove 'message #'who-called-me?)
-
-
-
- -

-Lastly, individual messages can be reenabled using the (advice-remove '<FUNCTION-NAME> #'suppress-messages) approach. Use this when you accidentally disabled a helpful message. -

- - -
-
-(defun suppress-messages (old-fun &rest args)
-  (cl-flet ((silence (&rest args1) (ignore)))
-    (advice-add 'message :around #'silence)
-    (unwind-protect
-        (apply old-fun args)
-      (advice-remove 'message #'silence))))
-
-(advice-add 'pixel-scroll-precision :around #'suppress-messages)
-(advice-add 'mu4e--server-filter :around #'suppress-messages)
-(advice-add 'org-unlogged-message :around #'suppress-messages)
-(advice-add 'magit-auto-revert-mode--init-kludge  :around #'suppress-messages)
-(advice-add 'push-mark  :around #'suppress-messages)
-
-;; to reenable
-;; (advice-remove 'timer-event-handler #'suppress-messages)
-
-(defun who-called-me? (old-fun format &rest args)
-  (let ((trace nil) (n 1) (frame nil))
-    (while (setf frame (backtrace-frame n))
-      (setf n     (1+ n)
-            trace (cons (cadr frame) trace)) )
-    (apply old-fun (concat "<<%S>>\n" format) (cons trace args))))
-
-;; enable to get message backtrace, the first function shown in backtrace calls the other functions
-;; (advice-add 'message :around #'who-called-me?)
-
-;; disable to stop receiving backtrace
-(advice-remove 'message #'who-called-me?)
-
-
-
-
-
-
-
-

3.1.8. Move up one directory for find-file

-
-

-I find it very annoying that the standard behavior for M-DEL only deletes one word when using find-file. This function makes it so that we always go up by one directory level instead. -

- -

-This function was found here: https://www.reddit.com/r/emacs/comments/re31i6/how_to_go_up_one_directory_when_using_findfile_cx/ -

- -
-
-(defun up-directory (path)
-  "Move up a directory in PATH without affecting the kill buffer."
-  (interactive "p")
-  (if (string-match-p "/." (minibuffer-contents))
-      (let ((end (point)))
-        (re-search-backward "/.")
-        (forward-char)
-        (delete-region (point) end))))
-
-(define-key minibuffer-local-filename-completion-map
-            [C-backspace] #'up-directory)
-
-
-
-
-
-
-

3.1.9. org-mode: General setup

-
-

-Sets up the basic settings that I want to have active in org-mode buffers. -

- -

-Used here: General org-mode -

- -
-
-(defun swarsel/org-mode-setup ()
-  (org-indent-mode)
-  (variable-pitch-mode 1)
-  ;;(auto-fill-mode 0)
-  (setq display-line-numbers-type 'relative
-        display-line-numbers-current-absolute 1
-        display-line-numbers-width-start nil
-        display-line-numbers-width 6
-        display-line-numbers-grow-only 1)
-  (add-hook 'org-tab-first-hook 'org-end-of-line)
-  (visual-line-mode 1))
-
-
-
-
-
-
-

3.1.10. org-mode: Visual-fill column

-
-

-This function sets the width of buffers in org-mode. -

- -

-Used in: Centered org-mode Buffers -

- -
-
-(defun swarsel/org-mode-visual-fill ()
-  (setq visual-fill-column-width 150
-        visual-fill-column-center-text t)
-  (visual-fill-column-mode 1))
-
-
-
-
-
- -
-

3.1.11. org-mode: Auto-tangle and export Configuration Files

-
-

-This section automatically tangles all configuration blocks in this file to the defined Emacs org-file. It also exports the configuration file as html. -

- - - -
-
-(defun swarsel/org-babel-tangle-config ()
-  (when (string-equal (buffer-file-name)
-                      swarsel-emacs-org-filepath)
-    ;; Dynamic scoping to the rescue
-    (let ((org-confirm-babel-evaluate nil))
-      (org-html-export-to-html)
-      (org-babel-tangle)))
-  (when (string-equal (buffer-file-name)
-                      swarsel-nix-org-filepath)
-    ;; Dynamic scoping to the rescue
-    (let ((org-confirm-babel-evaluate nil))
-      (org-babel-tangle))))
-
-(setq org-html-htmlize-output-type nil)
-
-(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config)))
-
-
-
-
-
-
-
- -
-

3.1.12. org-mode: Fold current heading

-
-

-Normally emacs cycles between three states: -

- -
    -
  1. fully folded
  2. -
  3. One heading expanded
  4. -
  5. All headings expanded
  6. -
- -

-However, I want to be able to fold a single heading consistently. -

- -
-
-(defun org-fold-outer ()
-  (interactive)
-  (org-beginning-of-line)
-  (if (string-match "^*+" (thing-at-point 'line t))
-      (outline-up-heading 1))
-  (outline-hide-subtree)
-  )
-
-
-
-
-
- -
-

3.1.13. corfu: Do not interrupt navigation

-
-

-These three functions allow me to keep using the normal navigation keys even when a corfu completion pops up. -

- -

-These functions are used here: Corfu -

- -
-
-(defun swarsel/corfu-normal-return (&optional arg)
-  (interactive)
-  (corfu-quit)
-  (newline)
-  )
-
-(defun swarsel/corfu-quit-and-up (&optional arg)
-  (interactive)
-  (corfu-quit)
-  (evil-previous-visual-line))
-
-(defun swarsel/corfu-quit-and-down (&optional arg)
-  (interactive)
-  (corfu-quit)
-  (evil-next-visual-line))
-
-
-
-
-
- -
-

3.1.14. python shell reloading

-
-

-The standard Emacs behaviour for the Python process shell is a bit annoying. This is my attempt at making it show automatically on opening a python buffer and making it refresh on its own as well. This does not nicely work yet. -

- -
-
-    ;; run the python inferior shell immediately upon entering a python buffer
-    ;; (add-hook 'python-mode-hook 'swarsel/run-python)
-
-  ;; (defun swarsel/run-python ()
-  ;;   (save-selected-window
-  ;;     (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command))))))
-
-;; reload python shell automatically
-(defun my-python-shell-run ()
-  (interactive)
-  (when (get-buffer-process "*Python*")
-     (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil)
-     (kill-process (get-buffer-process "*Python*"))
-     ;; Uncomment If you want to clean the buffer too.
-     ;;(kill-buffer "*Python*")
-     ;; Not so fast!
-     (sleep-for 0.5))
-  (run-python (python-shell-parse-command) nil nil)
-  (python-shell-send-buffer)
-  ;; Pop new window only if shell isnt visible
-  ;; in any frame.
-  (unless (get-buffer-window "*Python*" t)
-    (python-shell-switch-to-shell)))
-
-(defun my-python-shell-run-region ()
-  (interactive)
-  (python-shell-send-region (region-beginning) (region-end))
-  (python-shell-switch-to-shell))
-
-
-
-
-
-
- - -
-

3.2. Custom Keybindings

-
-

-This defines a set of keybinds that I want to have available globally. I have one set of keys that is globally available through the C-SPC prefix. This set is used mostly for functions that I have trouble remembering the original keybind for, or that I just want to have gathered in a common space. -

- -

-I also define some keybinds to some combinations directly. Those are used mostly for custom functions that I call often enough to warrant this. -

- -
-
-;; Make ESC quit prompts
-(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
-
-;; Set up general keybindings
-(use-package general
-  :config
-  (general-create-definer swarsel/leader-keys
-    :keymaps '(normal insert visual emacs)
-    :prefix "SPC"
-    :global-prefix "C-SPC")
-
-  (swarsel/leader-keys
-    "e"  '(:ignore e :which-key "evil")
-    "eo" '(evil-jump-backward :which-key "cursor jump backwards")
-    "eO" '(evil-jump-forward :which-key "cursor jump forwards")
-    "t"  '(:ignore t :which-key "toggles")
-    "ts" '(hydra-text-scale/body :which-key "scale text")
-    "te" '(swarsel/toggle-evil-state :which-key "emacs/evil")
-    "tl" '(display-line-numbers-mode :which-key "line numbers")
-    "tp" '(evil-cleverparens-mode :wk "cleverparens")
-    "to" '(olivetti-mode :wk "olivetti")
-    "td" '(darkroom-tentative-mode :wk "darkroom")
-    "tw" '((lambda () (interactive) (toggle-truncate-lines)) :which-key "line wrapping")
-    "m"  '(:ignore m :which-key "modes/programs")
-    "mm" '((lambda () (interactive) (mu4e)) :which-key "mu4e")
-    "mg" '((lambda () (interactive) (magit-list-repositories)) :which-key "magit-list-repos")
-    "mc" '((lambda () (interactive) (swarsel/open-calendar)) :which-key "calendar")
-    "mp" '(popper-toggle :which-key "popper")
-    "md" '(dirvish :which-key "dirvish")
-    "o"  '(:ignore o :which-key "org")
-    "op" '((lambda () (interactive) (org-present)) :which-key "org-present")
-    "ob" '((lambda () (interactive) (org-babel-mark-block)) :which-key "Mark whole src-block")
-    "ol" '((lambda () (interactive) (org-insert-link)) :which-key "insert link")
-    "os" '((lambda () (interactive) (org-store-link)) :which-key "store link")
-    "od" '((lambda () (interactive) (org-babel-demarcate-block)) :which-key "demarcate (split) src-block")
-    ;; "c"  '(:ignore c :which-key "capture")
-    ;; "cj" '((lambda () (interactive) (org-capture nil "jj")) :which-key "journal")
-    ;; "cs" '(markdown-download-screenshot :which-key "screenshot")
-    "l"  '(:ignore l :which-key "links")
-    "le" '((lambda () (interactive) (find-file swarsel-emacs-org-filepath)) :which-key "Emacs.org")
-    "ls" '((lambda () (interactive) (find-file "/smb:Swarsel@192.168.1.3:")) :which-key "Server")
-    "lo" '(dired swarsel-obsidian-vault-directory :which-key "obsidian")
-    ;; "la" '((lambda () (interactive) (find-file swarsel-org-anki-filepath)) :which-key "anki")
-    "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org")
-    "lp" '((lambda () (interactive) (projectile-switch-project)) :which-key "switch project")
-    "lg" '((lambda () (interactive) (magit-list-repositories)) :which-key "list git repos")
-    ;; "a"   '(:ignore a :which-key "anki")
-    ;; "ap"  '(anki-editor-push-tree :which-key "push new cards")
-    ;; "an"  '((lambda () (interactive) (org-capture nil "a")) :which-key "new card")
-    ;; "as"  '(swarsel-anki-set-deck-and-notetype :which-key "change deck and notetype")
-    "h"   '(:ignore h :which-key "help")
-    "hy"  '(yas-describe-tables :which-key "yas tables")
-    "hb"  '(embark-bindings :which-key "current key bindings")
-    "h"   '(:ignore t :which-key "describe")
-    "he"  'view-echo-area-messages
-    "hf"  'describe-function
-    "hF"  'describe-face
-    "hl"  '(view-lossage :which-key "show command keypresses")
-    "hL"  'find-library
-    "hm"  'describe-mode
-    "ho"  'describe-symbol
-    "hk"  'describe-key
-    "hK"  'describe-keymap
-    "hp"  'describe-package
-    "hv"  'describe-variable
-    "hd"  'devdocs-lookup
-    "w"   '(:ignore t :which-key "window")
-    "wl"  'windmove-right
-    "wh"  'windmove-left
-    "wk"  'windmove-up
-    "wj"  'windmove-down
-    "wr"  'winner-redo
-    "wd"  'delete-window
-    "w="  'balance-windows-area
-    "wD"  'kill-buffer-and-window
-    "wu"  'winner-undo
-    "wr"  'winner-redo
-    "w/"  'evil-window-vsplit
-    "w-"  'evil-window-split
-    "wm"  '(delete-other-windows :wk "maximize")
-    ))
-
-;; General often used hotkeys
-(general-define-key
- "C-M-a" (lambda () (interactive) (org-capture nil "a")) ; make new anki card
- ;; "C-M-d" 'swarsel-obsidian-daily ; open daily obsidian file and create if not exist
- ;; "C-M-S" 'swarsel-anki-set-deck-and-notetype ; switch deck and notetype for new anki cards
- ;; "C-M-s" 'markdown-download-screenshot ; wrapper for org-download-screenshot
- "C-c d" 'crux-duplicate-current-line-or-region
- "C-c D" 'crux-duplicate-and-comment-current-line-or-region
- "<DUMMY-m>" 'swarsel/last-buffer
- "M-\\" 'indent-region
- "C-<f9>" 'my-python-shell-run
- )
-
-
-
-
-
-
-

3.3. Directory setup

-
-

-In this section I setup some aliases that I use for various directories on my system. Some of these are actually used for magit repository finding etc., but many of them serve no real use and I need to clean this up someday. -

- -
-
-;; set Nextcloud directory for journals etc.
-(setq swarsel-sync-directory "~/Nextcloud"
-      swarsel-emacs-directory "~/.emacs.d"
-      swarsel-dotfiles-directory "~/.dotfiles"
-      swarsel-projects-directory "~/Documents/GitHub")
-
-(setq swarsel-emacs-org-filepath (expand-file-name "Emacs.org" swarsel-dotfiles-directory)
-      swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory))
-
-
-;; set Emacs main configuration .org names
-(setq swarsel-emacs-org-file "Emacs.org"
-      swarsel-anki-org-file "Anki.org"
-      swarsel-tasks-org-file "Tasks.org"
-      swarsel-archive-org-file "Archive.org"
-      swarsel-org-folder-name "Org"
-      swarsel-obsidian-daily-folder-name "⭐ Personal/Journal"
-      swarsel-obsidian-folder-name "Obsidian"
-      swarsel-obsidian-vault-name "Main")
-
-
-;; set directory paths
-(setq swarsel-org-directory (expand-file-name swarsel-org-folder-name  swarsel-sync-directory)) ; path to org folder
-(setq swarsel-obsidian-directory (expand-file-name swarsel-obsidian-folder-name swarsel-sync-directory)) ; path to obsidian
-(setq swarsel-obsidian-vault-directory (expand-file-name swarsel-obsidian-vault-name swarsel-obsidian-directory)) ; path to obsidian vault
-(setq swarsel-obsidian-daily-directory (expand-file-name swarsel-obsidian-daily-folder-name swarsel-obsidian-vault-directory)) ; path to obsidian daily folder
-
-;; filepaths to certain documents
-(setq swarsel-org-anki-filepath (expand-file-name swarsel-anki-org-file swarsel-org-directory) ; path to anki export file
-      swarsel-org-tasks-filepath (expand-file-name swarsel-tasks-org-file swarsel-org-directory)
-      swarsel-org-archive-filepath (expand-file-name swarsel-archive-org-file swarsel-org-directory))
-
-;; set paths to authentication files (forge)
-;; (setq auth-source-pass-filename "~/.local/share/password-store"
-(setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg")
-      auth-source-cache-expiry nil) ; default is 2h
-
-
-
-
-
-
-
- -
-

3.4. Unclutter .emacs.d

-
-

-In this section I move the custom.el out of it's standard location in .emacs.d. Firstly, I dislike using this file at all since I would rather have fully stateful configuration as commanded by this file. Secondly, this file is too easily permanently changed. Recently I figured out the last bits that I needed to remove from custom.el to no longer be reliant on it, so I now just write it to a temporary file (through make-temp=file) which will be cleaned on shutdown. However, I like to retain the custom framework because it is nice for testing out theme customizations, hence why I still load the file. -

- -

-This section also sets the emacs directory to the ~/.cache/ directory which is useful for files that I do not want to have lying around in my .emacs.d. -

- -
-
-;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d
-(setq user-emacs-directory (expand-file-name "~/.cache/emacs/")
-      url-history-file (expand-file-name "url/history" user-emacs-directory))
-
-;; Use no-littering to automatically set common paths to the new user-emacs-directory
-(use-package no-littering)
-(setq custom-file (make-temp-file "emacs-custom-"))
-(load custom-file t)
-
-
-
-
-
- -
-

3.5. Move backup files to another location

-
-

-Many people dislike the Emacs backup files; I do enjoy them, but have to admit that they clutter the filesystem a little too much. Also, I rarely need to access these over different sessions. Hence I move them to /tmp - if Emacs unexpectedly crashes, the files can be recovered, but the backup files will not gather everywhere and will be deleted upon shutdown. -

- -
-
-(let ((backup-dir "~/tmp/emacs/backups")
-      (auto-saves-dir "~/tmp/emacs/auto-saves/"))
-  (dolist (dir (list backup-dir auto-saves-dir))
-    (when (not (file-directory-p dir))
-      (make-directory dir t)))
-  (setq backup-directory-alist `(("." . ,backup-dir))
-        auto-save-file-name-transforms `((".*" ,auto-saves-dir t))
-        auto-save-list-file-prefix (concat auto-saves-dir ".saves-")
-        tramp-backup-directory-alist `((".*" . ,backup-dir))
-        tramp-auto-save-directory auto-saves-dir))
-
-(setq backup-by-copying t    ; Don't delink hardlinks
-      delete-old-versions t  ; Clean up the backups
-      version-control t      ; Use version numbers on backups,
-      kept-new-versions 5    ; keep some new versions
-      kept-old-versions 2)   ; and some old ones, too
-
-
-
-
-
-
-
-

4. General init.el setup + UI

-
-

-In this general section I have settings that I either consider to be integral to my experience when using emacs or have no other section that I feel they belong to. -

-
- -
-

4.1. General setup

-
-

-Here I set up some things that are too minor to put under other categories. -

-
    -
  • Firstly we disable to having to type `yes` and `no` and switch it to `y` and `n`.
  • -
  • We also enable the marking of trailing whitespaces.
  • -
  • Also, make emacs highlight the current line globally
  • -
  • Emacs defaults to pausing all display redrawing on any input. This may have been useful previously, but is not necessary nowadays.
  • -
  • I also disable the suspend-frame function, as I never use it and it is quite confusing when accidentally hitting the keys for it.
  • -
- -
-
-;; use UTF-8 everywhere
-(set-language-environment "UTF-8")
-
-;; set default font size
-(defvar swarsel/default-font-size 130)
-(setq swarsel-standard-font "FiraCode Nerd Font Mono"
-      swarsel-alt-font "FiraCode Nerd Font Mono")
-
-;; (defalias 'yes-or-no-p 'y-or-n-p)
-;;(setq-default show-trailing-whitespace t)
-(add-hook 'before-save-hook 'delete-trailing-whitespace)
-(global-hl-line-mode 1)
-;; (setq redisplay-dont-pause t) ;; obsolete
-(setq blink-cursor-mode nil) ;; blink-cursor is an unexpected source of slowdown
-(global-subword-mode 1) ; Iterate through CamelCase words
-(setq blink-matching-paren nil) ;; this makes the cursor jump around annoyingly
-(delete-selection-mode 1)
-(setq vc-follow-symlinks t)
-(setq require-final-newline t)
-(winner-mode 1)
-(setq load-prefer-newer t)
-
-(setq undo-limit 80000000
-      evil-want-fine-undo t
-      auto-save-default t
-      password-cache-expiry nil
-      )
-(setq browse-url-browser-function 'browse-url-firefox)
-;; disable a keybind that does more harm than good
-(global-set-key [remap suspend-frame]
-                (lambda ()
-                  (interactive)
-                  (message "This keybinding is disabled (was 'suspend-frame')")))
-
-(setq visible-bell nil)
-(setq initial-major-mode 'fundamental-mode
-      initial-scratch-message nil)
-
-(add-hook 'prog-mode-hook 'display-line-numbers-mode)
-(add-hook 'text-mode-hook 'display-line-numbers-mode)
-
-
-
-
-
- -
-

4.2. Mark all themes as safe

-
-

-Normally when switching themes in emacs, the user will be warned that themes can run malicious code. I only run one theme really and deem it safe. It is however annoying to be asked this on every new system and it also creates lines in custom.el to answer that query, so here I declare all themes as safe. -

- -
-
-(setq custom-safe-themes t)
-
-
-
-
-
-
-

4.3. Show less compilation warnings

-
-

-When Emacs compiles stuff, it often shows a bunch of warnings that I do not need to deal with. Here we silence those. Some will be disabled completely, and some only when we have native compilation available (which should be most of the time, however). -

- -
-
-(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
-;; Make native compilation silent and prune its cache.
-(when (native-comp-available-p)
-  (setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation
-  (setq native-compile-prune-cache t)) ; Emacs 29
-
-
-
-
-
- -
-

4.4. Indentation

-
-

-Here I define several options related to indentation; I first make it so that only whitespace will be used instead of tab characters for indentation, and I also set a small standard indent. -

- -

-We set tab-always-indent to 'complete in order to indent first and then do completion if there are any. Also we make it so that python will not complain about missing indentation info. -

- -

-Lastly, I load the highlight-indent-guides package. This adds a neat visual indicator of the indentation level, which is useful for languages like python. -

- -
-
-(setq-default indent-tabs-mode nil
-              tab-width 2)
-
-(setq tab-always-indent 'complete)
-(setq python-indent-guess-indent-offset-verbose nil)
-
-(use-package highlight-indent-guides
-  :hook (prog-mode . highlight-indent-guides-mode)
-  :init
-  (setq highlight-indent-guides-method 'column)
-  (setq highlight-indent-guides-responsive 'top)
-  )
-
-(with-eval-after-load 'highlight-indent-guides
-  (set-face-attribute 'highlight-indent-guides-even-face nil :background "gray10")
-  (set-face-attribute 'highlight-indent-guides-odd-face nil :background "gray20")
-  (set-face-attribute 'highlight-indent-guides-stack-even-face nil :background "gray40")
-  (set-face-attribute 'highlight-indent-guides-stack-odd-face nil :background "gray50"))
-
-
-
-
- -
-

4.5. Scrolling

-
-

-By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying. This sets up more granular scrolling that allows scrolling with a mouse wheel or the two-finger touchscreen gesture. This now also works in buffers with a very small frame. -

- -
-
-(setq mouse-wheel-scroll-amount
-      '(1
-        ((shift) . 5)
-        ((meta) . 0.5)
-        ((control) . text-scale))
-      mouse-drag-copy-region nil
-      make-pointer-invisible t
-      mouse-wheel-progressive-speed t
-      mouse-wheel-follow-mouse t)
-
-(setq-default scroll-preserve-screen-position t
-              scroll-conservatively 1
-              scroll-margin 0
-              next-screen-context-lines 0)
-
-(pixel-scroll-precision-mode 1)
-
-
-
-
-
- -
-

4.6. Evil

-
-
- -
-

4.6.1. General evil

-
-

-This setups up evil, which brings vim-like keybindings to emacs. In the same location, I also unbind the C-z key (I am very unhappy with this implementation, but it is the only thing that works consistently so far) to make it available for cape later. -

- -

-Also, I setup initial modes for several major-modes depending on what I deem fit. -

- -
-
-;; Emulate vim in emacs
-(use-package evil
-  :init
-  (setq evil-want-integration t) ; loads evil
-  (setq evil-want-keybinding nil) ; loads "helpful bindings" for other modes
-  (setq evil-want-C-u-scroll t) ; scrolling using C-u
-  (setq evil-want-C-i-jump nil) ; jumping with C-i
-  (setq evil-want-Y-yank-to-eol t) ; give Y some utility
-  (setq evil-shift-width 2) ; uniform indent
-  (setq evil-respect-visual-line-mode t) ; i am torn on this one
-  (setq evil-split-window-below t)
-  (setq evil-vsplit-window-right t)
-  :config
-  (evil-mode 1)
-  (define-key evil-normal-state-map (kbd "C-z") nil)
-  (define-key evil-insert-state-map (kbd "C-z") nil)
-  (define-key evil-visual-state-map (kbd "C-z") nil)
-  (define-key evil-motion-state-map (kbd "C-z") nil)
-  (define-key evil-operator-state-map (kbd "C-z") nil)
-  (define-key evil-replace-state-map (kbd "C-z") nil)
-  (define-key global-map (kbd "C-z") nil)
-  (evil-set-undo-system 'undo-tree)
-
-  ;; Don't use evil-mode in these contexts, or use it in a specific mode
-  (evil-set-initial-state 'messages-buffer-mode 'emacs)
-  (evil-set-initial-state 'dashboard-mode 'emacs)
-  (evil-set-initial-state 'dired-mode 'emacs)
-  (evil-set-initial-state 'cfw:details-mode 'emacs)
-  (evil-set-initial-state 'Custom-mode 'emacs) ; god knows why this mode is in uppercase
-  (evil-set-initial-state 'mu4e-headers-mode 'normal)
-  (evil-set-initial-state 'python-inferior-mode 'normal)
-  (add-hook 'org-capture-mode-hook 'evil-insert-state)
-  (add-to-list 'evil-buffer-regexps '("COMMIT_EDITMSG" . insert)))
-
-
-
-
-
-
-

4.6.2. evil-collection

-
-

-This gives support for many different modes, and works beautifully out of the box. -

- -
-
-(use-package evil-collection
-  :after evil
-  :config
-  (evil-collection-init)
-  (setq forge-add-default-bindings nil))
-
-
-
-
-
-

4.6.3. evil-snipe

-
-

-This package changes the char-search commands like f by showing the results in a more visual manner. It also gives a 2-character search using s and S. -

- -
-
;; enables 2-char inline search
-  (use-package evil-snipe
-    :after evil
-    :demand
-    :config
-    (evil-snipe-mode +1)
-    ;; replace 1-char searches (f&t) with this better UI
-    (evil-snipe-override-mode +1))
-
-
-
-
- -
-

4.6.4. evil-cleverparens

-
-

-This helps keeping parentheses balanced which is useful when writing in languages like Elisp. I do not activate this by default, as most languages do not profit from this enough in my eyes. -

- -
-
;; for parentheses-heavy languades modify evil commands to keep balance of parantheses
-(use-package evil-cleverparens)
-
-
-
-
-
- -
-

4.6.5. evil-surround

-
-

-This minor-mode adds functionality for doing better surround-commands; for example ci[ will let you change the word within square brackets. -

- -
-
-;; enables surrounding text with S
-(use-package evil-surround
-  :config
-  (global-evil-surround-mode 1))
-
-
-
-
-
-
- -
-

4.7. ispell

-
-

-This should setup a wordlist that can be used as a dictionary. However, for some reason this does not work, and I will need to further investigate this issue. -

- -
-
-;; set the NixOS wordlist by hand
-(setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST")
-
-
-
-
-
- -
-

4.8. Font Configuration

-
-

-Here I define my fonts to be used. Honestly I do not understand the face-attributes and pitches of emacs all too well. It seems this configuration works fine, but I might have to revisit this at some point in the future. -

- -
-
-(dolist (face '(default fixed-pitch))
-  (set-face-attribute face nil
-                      :font "FiraCode Nerd Font Mono"))
-(add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono"))
-
-(set-face-attribute 'default nil :height 100)
-(set-face-attribute 'fixed-pitch nil :height 1.0)
-
-(set-face-attribute 'variable-pitch nil
-                    :family "IBM Plex Sans"
-                    :weight 'regular
-                    :height 1.06)
-
-;; these settings used to be in custom.el
-
-
-
-
-
- -
-

4.9. Theme

-
-

-I have grown to love the doom-citylights theme and have modeled my whole system after it. Also solaire-mode is a nice mode that inverts the alt-faces with the normal faces for specific 'minor' buffers (like Help-buffers). -

- -
-
-(use-package solaire-mode
-  :custom
-  (solaire-global-mode +1))
-
-(use-package doom-themes
-  :hook
-  (server-after-make-frame . (lambda () (load-theme
-                                         'doom-city-lights t)))
-  :config
-  (load-theme 'doom-city-lights t)
-  (doom-themes-treemacs-config)
-  (doom-themes-org-config))
-
-
-
-
-
- -
-

4.10. Icons

-
-

-This section loads the base icons used in my configuration. I am using nerd-icons over all-the-icons since the former seems to have more integrations with different packages than the latter. -

- -

-Used in: -

- - -
-
-(use-package nerd-icons)
-
-
-
-
-
- -
-

4.11. Variable Pitch Mode

-
-

-This minor mode allows mixing fixed and variable pitch fonts within the same buffer. -

- -
-
-(use-package mixed-pitch
-  :custom
-  (mixed-pitch-set-height nil)
-  (mixed-pitch-variable-pitch-cursor nil)
-  :hook
-  (text-mode . mixed-pitch-mode))
-
-
-
-
-
-
- -
-

4.12. Modeline

-
-

-Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline. Most informations I disable for it, except for the cursor information (row + column) as well as a widget for mu4e and git information. -

- -
-
-(use-package doom-modeline
-  :init
-  (doom-modeline-mode)
-  (column-number-mode)
-  :custom
-  ((doom-modeline-height 22)
-   (doom-modeline-indent-info nil)
-   (doom-modeline-buffer-encoding nil)))
-
-
-
-
-
-
- -
-

4.13. Helper Modes

-
-
-
-

4.13.1. Vertico, Orderless, Marginalia, Consult, Embark

-
-

-This set of packages uses the default emacs completion framework and works together to provide a very nice user experience: -

- -
    -
  • Vertico simply provides a vertically stacking completion
  • -
  • Marginalia adds more information to completion results
  • -
  • Orderless allows for fuzzy matching
  • -
  • Consult provides better implementations for several user functions, e.g. consult-line or consult-outline
  • -
  • Embark allows acting on the results in the minibuffer while the completion is still ongoing - this is extremely useful since it allows to, for example, read the documentation for several functions without closing the help search. It can also collect the results of a grep operation into a seperate buffer that edits the result in their original location.
  • -
- -

-Nerd icons is originally enabled here: Icons -

-
- -
    -
  1. vertico
    -
    -
    -
    -(setq read-buffer-completion-ignore-case t
    -      read-file-name-completion-ignore-case t
    -      completion-ignore-case t)
    -
    -(use-package vertico
    -  :custom
    -  (vertico-scroll-margin 0)
    -  (vertico-count 10)
    -  (vertico-resize t)
    -  (vertico-cycle t)
    -  :init
    -  (vertico-mode)
    -  (vertico-mouse-mode))
    -
    -
    -
    -
  2. - -
  3. vertico-directory
    -
    -

    -This package allows for Ido-like directory navigation. -

    - -
    -
    (use-package vertico-directory
    -  :ensure nil
    -  :after vertico
    -  :bind (:map vertico-map
    -              ("RET" . vertico-directory-enter)
    -              ("C-DEL" . vertico-directory-delete-word)
    -              ("DEL" . vertico-directory-delete-char))
    -  ;; Tidy shadowed file names
    -  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
    -
    -
    -
    -
    -
  4. - -
  5. orderless
    -
    -

    -The completion styles that I chose here can possibly still be improved. I need to spend more time on this. -

    - -
    -
    -(use-package orderless
    -  :custom
    -  (completion-styles '(orderless flex basic))
    -  (completion-category-overrides '((file (styles . (partial-completion)))
    -                                   (eglot (styles orderless)))))
    -
    -
    -
    -
    -
  6. - -
  7. consult
    -
    -

    -The big winner here are the convenient keybinds being setup here for general use. Also, I setup vim-navigation for minibuffer completions. consult-buffer is set twice because I am still used to that weird C-M-j command that I chose for ivy-switch-buffer when I first started using Emacs. I want to move to the other command but for now it is not feasible to delete the other one. -

    - -
    -
    -(use-package consult
    -  :config
    -  (setq consult-fontify-max-size 1024)
    -  :bind
    -  (("C-x b" . consult-buffer)
    -   ("C-c <C-m>" . consult-global-mark)
    -   ("C-c C-a" . consult-org-agenda)
    -   ("C-x O" . consult-org-heading)
    -   ("C-M-j" . consult-buffer)
    -   ("C-s" . consult-line)
    -   ("M-g M-g" . consult-goto-line)
    -   ("M-g i" . consult-imenu)
    -   ("M-s M-s" . consult-line-multi)
    -   :map minibuffer-local-map
    -   ("C-j" . next-line)
    -   ("C-k" . previous-line)))
    -
    -
    -
    -
    -
  8. -
  9. embark
    -
    -

    -I have stripped down the embark keybinds heavily. It is very useful to me even in it's current state, but it quickly becomes overwhelming. embark-dwim acts on a candidate without closing the minibuffer, which is very useful. embark-act lets the user choose from all actions, but has an overwhelming interface. -

    - -
    -
    -(use-package embark
    -  :bind
    -  (("C-." . embark-act)
    -   ("M-." . embark-dwim)
    -   ("C-h B" . embark-bindings)
    -   ("C-c c" . embark-collect))
    -  :custom
    -  (prefix-help-command #'embark-prefix-help-command)
    -  (embark-quit-after-action '((t . nil)))
    -  :config
    -  (add-to-list 'display-buffer-alist
    -               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
    -                 nil
    -                 (window-parameters (mode-line-format . none)))))
    -
    -
    -
    -
    -
  10. -
  11. embark-consult
    -
    -

    -Provides previews for embark. -

    - -
    -
    -(use-package embark-consult
    -  :after (embark consult)
    -  :demand t ; only necessary if you have the hook below
    -  ;; if you want to have consult previews as you move around an
    -  ;; auto-updating embark collect buffer
    -  :hook
    -  (embark-collect-mode . consult-preview-at-point-mode))
    -
    -
    -
    -
  12. -
  13. marginalia
    -
    -

    -I set the annotation-mode of marginalia to heavy. This gives even more information on the stuff that you are looking at. One thing I am missing from ivy is the highlighting on mode-commands based on the current state of the mode. Also, I do not understand all the shorthands used by marginalia yet. -

    - -
    -
    (use-package marginalia
    -  :after vertico
    -  :init
    -  (marginalia-mode)
    -  (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)))
    -
    -
    -
    -
    -
  14. -
  15. nerd-icons-completion
    -
    -

    -As stated above, this simply provides nerd-icons to the completion framework. -

    - -
    -
    -(use-package nerd-icons-completion
    -  :after (marginalia nerd-icons)
    -  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
    -  :init
    -  (nerd-icons-completion-mode))
    -
    -
    -
    -
    -
    -
  16. -
-
- -
-

4.13.2. Helpful + which-key: Better help defaults

-
-

-This pair of packages provides information on keybinds in addition to function names, which makes it easier to remember keybinds (which-key). The helpful package provides a better Help framework for Emacs. For some reason, the Help windows are always being focused by the cursor even though I have set help-window-select to nil. I do not understand why. -

- -
-
-(use-package which-key
-  :init (which-key-mode)
-  :diminish which-key-mode
-  :config
-  (setq which-key-idle-delay 0.3))
-
-(use-package helpful
-  :bind
-  (("C-h f" . helpful-callable)
-   ("C-h v" . helpful-variable)
-   ("C-h k" . helpful-key)
-   ("C-h C-." . helpful-at-point))
-  :config
-  (setq help-window-select nil))
-
-
-
-
-
- -
-

4.14. Ligatures

-
-

-Personally, I think ligatures are fancy. With this mode, they stay 'cursorable'. However, I do not need them in all modes, so I only use them in programming modes. -

- -
-
-(use-package ligature
-  :init
-  (global-ligature-mode t)
-  :config
-  (ligature-set-ligatures 'prog-mode
-                          '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
-                            ":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
-                            "!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
-                            "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
-                            "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
-                            "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
-                            "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
-                            "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
-                            ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
-                            "<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
-                            "##" "#(" "#?" "#_" "%%" ".=" ".." ".?" "+>" "++" "?:" "?="
-                            "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\"
-                            "://" ";;")))
-
-
-
-
-
- -
-

4.15. Popup (popper) + Shackle Buffers

-
-

-The popper package allows to declare different buffers as 'popup-type', which sort of acts like a scratchpad. It can be toggled at any time using popper-toggle and the resulting frame can be freely customized (with shackle) to a certain size. It is also possible to prevent a buffer from appearing - I do this for example to the *Warnings* buffer, since usually I am not interested in it's output. -

- -

-popper-echo-mode shows all buffers that are currently stored as a popup in the echo area when a popup is opened - this is useful since you can cycle between all popup buffers. -

- -
-
-(use-package popper
-  :bind (("M-["   . popper-toggle))
-  :init
-  (setq popper-reference-buffers
-        '("\\*Messages\\*"
-          ("\\*Warnings\\*" . hide)
-          "Output\\*$"
-          "\\*Async Shell Command\\*"
-          "\\*Async-native-compile-log\\*"
-          help-mode
-          helpful-mode
-          "*Occur*"
-          "*scratch*"
-          "*julia*"
-          "*Python*"
-          ;; ("*tex-shell*" . hide)
-          (compilation-mode . hide)))
-  (popper-mode +1)
-  (popper-echo-mode +1))
-
-(use-package shackle
-  :config
-  (setq shackle-rules '(("*Messages*" :select t :popup t :align right :size 0.3)
-                        ("*Warnings*" :ignore t :popup t :align right :size 0.3)
-                        ("*Occur*" :select t :popup t :align below :size 0.2)
-                        ("*scratch*" :select t :popup t :align below :size 0.2)
-                        ("*Python*" :select t :popup t :align below :size 0.2)
-                        ("*tex-shell*" :ignore t :popup t :align below :size 0.2)
-                        (helpful-mode :select t :popup t :align right :size 0.35)
-                        (help-mode :select t :popup t :align right :size 0.4)))
-  (shackle-mode 1))
-
-
-
-
-
-
-

4.16. Indicate first and last line of buffer

-
-

-This places little angled indicators on the fringe of a window which indicate buffer boundaries. This is not super useful, but makes use of a space that I want to keep for aesthetic reasons anyways and makes it a bit more useful in the process. -

- -
-
-(setq-default indicate-buffer-boundaries t)
-
-
-
-
-
-
- -
-

5. Modules

-
-

-This section houses all configuration bits that are related to a specific package that is not fundamental to my Emacs experience. -

- -

-At some point this will receive further sorting, but for now this is good enough. -

-
- -
-

5.1. Org Mode

-
-

-org-mode is probably my most-used mode in Emcas. It acts as my organizer, config management tool and calender even. -

- -

-Note that nearly all headings within the Org-mode heading are coded within the use-package setup, so be very careful about moving stuff about here. -

-
- -
-

5.1.1. General org-mode

-
-

-This sets up the basic org-mode. I wrote a function to handle some of the initial org-mode behaviour in org-mode setup. - -This part of the configuration mostly makes some aesthetic changes, enables neat LaTeX and points Emacs to some files that it needs for org-mode -

- -
-
-(use-package org
-  ;;:diminish (org-indent-mode)
-  :hook (org-mode . swarsel/org-mode-setup)
-  :bind
-  (("C-<tab>" . org-fold-outer)
-  ("C-c s" . org-store-link))
-  :config
-  (setq org-ellipsis " ⤵"
-        org-link-descriptive t
-        org-hide-emphasis-markers t)
-  (setq org-startup-folded t)
-  (setq org-support-shift-select t)
-
-  ;; (setq org-agenda-start-with-log-mode t)
-  ;; (setq org-log-done 'time)
-  ;; (setq org-log-into-drawer t)
-  (setq org-startup-with-inline-images t)
-  (setq org-image-actual-width nil)
-  (setq org-format-latex-options '(:foreground "White" :background default :scale 2.0 :html-foreground "Black" :html-background "Transparent" :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\[")))
-
-
-
-
-
-
-

5.1.2. org-agenda

-
-

-Here I setup a plethora of keywords, keybinds and paths to give my org-agenda more power. -

- -
-
-(setq org-agenda-files '("/home/swarsel/Nextcloud/Org/Tasks.org"
-                         "/home/swarsel/Nextcloud/Org/Archive.org"
-                         "/home/swarsel/Nextcloud/Org/Anki.org"
-                         "/home/swarsel/Calendars/leon_cal.org"))
-
-(setq org-refile-targets
-      '((swarsel-archive-org-file :maxlevel . 1)
-        (swarsel-anki-org-file :maxlevel . 1)
-        (swarsel-tasks-org-file :maxlevel . 1)))
-
-(setq org-todo-keywords
-      '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
-        (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)")))
-
-
-;; Configure custom agenda views
-(setq org-agenda-custom-commands
-      '(("d" "Dashboard"
-         ((agenda "" ((org-deadline-warning-days 7)))
-          (todo "NEXT"
-                ((org-agenda-overriding-header "Next Tasks")))
-          (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))
-
-        ("n" "Next Tasks"
-         ((todo "NEXT"
-                ((org-agenda-overriding-header "Next Tasks")))))
-
-        ("W" "Work Tasks" tags-todo "+work-email")
-
-
-        ("w" "Workflow Status"
-         ((todo "WAIT"
-                ((org-agenda-overriding-header "Waiting on External")
-                 (org-agenda-files org-agenda-files)))
-          (todo "REVIEW"
-                ((org-agenda-overriding-header "In Review")
-                 (org-agenda-files org-agenda-files)))
-          (todo "PLAN"
-                ((org-agenda-overriding-header "In Planning")
-                 (org-agenda-todo-list-sublevels nil)
-                 (org-agenda-files org-agenda-files)))
-          (todo "BACKLOG"
-                ((org-agenda-overriding-header "Project Backlog")
-                 (org-agenda-todo-list-sublevels nil)
-                 (org-agenda-files org-agenda-files)))
-          (todo "READY"
-                ((org-agenda-overriding-header "Ready for Work")
-                 (org-agenda-files org-agenda-files)))
-          (todo "ACTIVE"
-                ((org-agenda-overriding-header "Active Projects")
-                 (org-agenda-files org-agenda-files)))
-          (todo "COMPLETED"
-                ((org-agenda-overriding-header "Completed Projects")
-                 (org-agenda-files org-agenda-files)))
-          (todo "CANC"
-                ((org-agenda-overriding-header "Cancelled Projects")
-                 (org-agenda-files org-agenda-files)))))))
-
-
-
-
-
-
-
-

5.1.3. org capture templates

-
-

-I wrote these capture templates to allow myself to quickly create Anki cards from within Emacs. I nearly never use this feature, but it cannot hurt to have. -

- -
-
-(setq org-capture-templates
-      `(
-        ("a" "Anki basic"
-         entry
-         (file+headline swarsel-org-anki-filepath "Dispatch")
-         (function swarsel-anki-make-template-string))
-
-        ("A" "Anki cloze"
-         entry
-         (file+headline org-swarsel-anki-file "Dispatch")
-         "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: Cloze\n:ANKI_DECK: 🦁 All::01 ❤️ Various::00 ✨ Allgemein\n:END:\n** Text\n%?\n** Extra\n")
-        ("t" "Tasks / Projects")
-        ("tt" "Task" entry (file+olp swarsel-org-tasks-filepath "Inbox")
-         "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)
-        ))
-)
-
-
-
-
- -
-

5.1.4. Font Faces

-
-

-Again, my understanding of the font-faces in Emacs is limited. This is mostly just tuned so that my org-files look acceptable. -

- -
-
-
-
-;; Set faces for heading levels
-(with-eval-after-load 'org-faces  (dolist (face '((org-level-1 . 1.1)
-                                                  (org-level-2 . 0.9)
-                                                  (org-level-3 . 0.9)
-                                                  (org-level-4 . 0.9)
-                                                  (org-level-5 . 0.9)
-                                                  (org-level-6 . 0.9)
-                                                  (org-level-7 . 0.9)
-                                                  (org-level-8 . 0.9)))
-                                    (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
-
-                      ;; Ensure that anything that should be fixed-pitch in Org files appears that way
-                      (set-face-attribute 'org-block nil   :inherit 'fixed-pitch)
-                      (set-face-attribute 'org-table nil   :inherit 'fixed-pitch)
-                      (set-face-attribute 'org-formula nil   :inherit 'fixed-pitch)
-                      (set-face-attribute 'org-code nil :inherit '(shadow 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))
-
-
-
-
-
- -
-

5.1.5. org-appear

-
-

-This package makes emphasis-markers appear when the cursor moves over them. Very useful as I enjoy the clean look of not always seeing them, but it is annoying not to be able to edit them properly. -

- -
-
-(use-package org-appear
-  :hook (org-mode . org-appear-mode)
-  :init
-  (setq org-appear-autolinks t)
-  (setq org-appear-autokeywords t)
-  (setq org-appear-autoentities t)
-  (setq org-appear-autosubmarkers t))
-
-
-
-
-
- -
-

5.1.6. Centered org-mode Buffers

-
-

-I like org-mode buffers to be centered, as I do not find that enormous lines are of big use. -

- -

-Function definition in: Visual-fill column -

- -
-
-(use-package visual-fill-column
-  :hook (org-mode . swarsel/org-mode-visual-fill))
-
-
-
-
-
- -
-

5.1.7. Fix headings not folding sometimes

-
-

-There is a weird bug in org-mode that makes it so that headings were not folding correctly sometimes. This setting seems to fix it. -

- -
-
-(setq org-fold-core-style 'overlays)
-
-
-
-
-
- -
-

5.1.8. Babel

-
-

-org-babel allows to run blocks in other programming languages within an org-mode buffer, similar to what e.g. jupyterhub offers for python. -

- -

-It also offers a very useful utility of exporting org-mode buffers to different formats; the feature I enjoy most is what makes this file useful: the tangling functionality. -

-
- -
    -
  1. Language Configuration
    -
    -
      -
    • This configures the languages that babel recognizes.
    • -
    - -
    -
    -(org-babel-do-load-languages
    - 'org-babel-load-languages
    - '((emacs-lisp . t)
    -   (python . t)
    -   (shell . t)
    -   ))
    -
    -(push '("conf-unix" . conf-unix) org-src-lang-modes)
    -
    -
    -
    -
    -
  2. - -
  3. old easy structure templates
    -
    -
      -
    • -org 9.2 changed the way structure templates work. This brings back the old way it worked. -

      - -

      -Usage: Type <, followed by one of the below keywords and press RET. The corresponding source block should appear. -

      - -
      -
      -(require 'org-tempo)
      -(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
      -(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
      -(add-to-list 'org-structure-template-alist '("py" . "src python :results output"))
      -(add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle"))
      -
      -
      -
    • -
    -
    -
  4. -
-
- -
-

5.1.9. aucTex

-
-

-This provides several utilities for LaTeX in Emacs, including many completions and convenience functions for math-mode. -

- -
-
-(use-package auctex)
-(setq TeX-auto-save t)
-(setq TeX-save-query nil)
-(setq TeX-parse-self t)
-  (setq-default TeX-master nil)
-
-(add-hook 'LaTeX-mode-hook 'visual-line-mode)
-(add-hook 'LaTeX-mode-hook 'flyspell-mode)
-(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
-(add-hook 'LaTeX-mode-hook 'reftex-mode)
-(setq LaTeX-electric-left-right-brace t)
-(setq font-latex-fontify-script nil)
-(setq TeX-electric-sub-and-superscript t)
-  ;; (setq reftex-plug-into-AUCTeX t)
-
-
-
-
-
- -
-

5.1.10. org-download

-
-

-This package allows to download and copy images into org-mode buffers. Sadly it does not work in a very stable manner - if you copy images that are also links to another page (like is often the case in a Google image search), Emacs might crash from this. -

- -
-
-(use-package org-download
-  :after org
-  :defer nil
-  :custom
-  (org-download-method 'directory)
-  (org-download-image-dir "./images")
-  (org-download-heading-lvl 0)
-  (org-download-timestamp "org_%Y%m%d-%H%M%S_")
-  ;;(org-image-actual-width 500)
-  (org-download-screenshot-method "grim -g \"$(slurp)\" %s")
-  :bind
-  ("C-M-y" . org-download-screenshot)
-  :config
-  (require 'org-download))
-
-
-
-
-
- -
-

5.1.11. org-fragtog

-
-

-This package automatically toggles LaTeX-fragments in org-files. It seems to also work in markdown-files which is a nice addition, as my Obsidian notes are held in markdown. -

- -
-
-(use-package org-fragtog)
-(add-hook 'org-mode-hook 'org-fragtog-mode)
-(add-hook 'markdown-mode-hook 'org-fragtog-mode)
-
-
-
-
-
- -
-

5.1.12. org-modern

-
-

-This just makes org-mode a little bit more beautiful, mostly by making the begin_src and end_src tags in source-blocks turn into more beautiful icons, as well as hiding #+ tags before them, as well as in the properties section of the file. -

- -
-
-(use-package org-modern
-  :config (setq org-modern-block-name
-                '((t . t)
-                  ("src" "»" "∥")))
-  :hook (org-mode . org-modern-mode))
-
-
-
-
-
- -
-

5.1.13. Presentations

-
-

-Recently I have grown fond of holding presentations using Emacs :) -

- -
-
-    (use-package org-present
-    :bind (:map org-present-mode-keymap
-           ("q" . org-present-quit)
-           ("<left>" . swarsel/org-present-prev)
-           ("<up>" . 'ignore)
-           ("<down>" . 'ignore)
-           ("<right>" . swarsel/org-present-next))
-    :hook ((org-present-mode . swarsel/org-present-start)
-           (org-present-mode-quit . swarsel/org-present-end))
-    )
-
-
-    (use-package hide-mode-line)
-
-    (defun swarsel/org-present-start ()
-      (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-verbatim (:height 1.55) org-verbatim)
-                                         (org-block (:height 1.25) org-block)
-                                         (org-block-begin-line (:height 0.7) org-block)
-                                         ))
-      (dolist (face '((org-level-1 . 1.1)
-                                                    (org-level-2 . 1.2)
-                                                    (org-level-3 . 1.2)
-                                                    (org-level-4 . 1.2)
-                                                    (org-level-5 . 1.2)
-                                                    (org-level-6 . 1.2)
-                                                    (org-level-7 . 1.2)
-                                                    (org-level-8 . 1.2)))
-                                      (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
-
-      (setq header-line-format " ")
-      (setq visual-fill-column-width 90)
-      (setq indicate-buffer-boundaries nil)
-      (setq inhibit-message nil)
-      (breadcrumb-mode 0)
-      (org-display-inline-images)
-      (global-hl-line-mode 0)
-      (display-line-numbers-mode 0)
-      (org-modern-mode 0)
-      (evil-insert-state 1)
-      (beginning-of-buffer)
-      (org-present-read-only)
-      ;; (org-present-hide-cursor)
-      (swarsel/org-present-slide)
-      )
-
-    (defun swarsel/org-present-end ()
-           (setq-local face-remapping-alist '((default variable-pitch default)))
-           (dolist (face '((org-level-1 . 1.1)
-                                                    (org-level-2 . 0.9)
-                                                    (org-level-3 . 0.9)
-                                                    (org-level-4 . 0.9)
-                                                    (org-level-5 . 0.9)
-                                                    (org-level-6 . 0.9)
-                                                    (org-level-7 . 0.9)
-                                                    (org-level-8 . 0.9)))
-                                      (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
-           (setq header-line-format nil)
-           (setq visual-fill-column-width 150)
-           (setq indicate-buffer-boundaries t)
-           (setq inhibit-message nil)
-           (breadcrumb-mode 1)
-           (global-hl-line-mode 1)
-           (display-line-numbers-mode 1)
-           (org-remove-inline-images)
-           (org-modern-mode 1)
-           (evil-normal-state 1)
-           ;; (org-present-show-cursor)
-           )
-
-  (defun swarsel/org-present-slide ()
-    (org-overview)
-    (org-show-entry)
-    (org-show-children)
-      )
-
-  (defun swarsel/org-present-prev ()
-    (interactive)
-    (org-present-prev)
-    (swarsel/org-present-slide))
-
-  (defun swarsel/org-present-next ()
-    (interactive)
-    (unless (eobp)
-    (org-next-visible-heading 1)
-    (org-fold-show-entry))
-    (when (eobp)
-    (org-present-next)
-    (swarsel/org-present-slide)
-    ))
-
-(defun clojure-leave-clojure-mode-function ()
- )
-
-(add-hook 'buffer-list-update-hook #'clojure-leave-clojure-mode-function)
-    (add-hook 'org-present-mode-hook 'swarsel/org-present-start)
-    (add-hook 'org-present-mode-quit-hook 'swarsel/org-present-end)
-    (add-hook 'org-present-after-navigate-functions 'swarsel/org-present-slide)
-
-
-
-
-
-
-
-

5.2. Nix Mode

-
-

-This adds a rudimentary nix-mode to Emacs. I have not really tried this out, as I am mostly editing nix-files in org-mode anyways. -

- -
-
-(use-package nix-mode
-  :mode "\\.nix\\'")
-
-
-
-
-
- -
-

5.3. Markdown Mode

-
-
-
-

5.3.1. Mode

-
-
-
-(setq markdown-command "pandoc")
-
-(use-package markdown-mode
-  :ensure t
-  :mode ("README\\.md\\'" . gfm-mode)
-  :init (setq markdown-command "multimarkdown")
-  :bind (:map markdown-mode-map
-              ("C-c C-e" . markdown-do)))
-
-
-
-
-
- -
-

5.3.2. LaTeX in Markdown

-
-
-
-(add-hook 'markdown-mode-hook
-          (lambda ()
-            (local-set-key (kbd "C-c C-x C-l") 'org-latex-preview)
-            (local-set-key (kbd "C-c C-x C-u") 'markdown-toggle-url-hiding)
-            ))
-
-
-
-
-
-
- -
-

5.4. Olivetti

-
-

-Olivetti is a mode specialized for writing prose in Emacs. I went for a very simple setup with little distractions. -

- -

-This mode is not automatically activated anywhere because I only rarely need it. -

- -
-
-(use-package olivetti
-  :init
-  (setq olivetti-body-width 100)
-  (setq olivetti-recall-visual-line-mode-entry-state t))
-
-
-
-
-
- -
-

5.5. darkroom

-
-

-Darkroom is package that reduces all forms of distraction to a minimum - this can be useful when simply reading a file for example. For this mode I have increased the text scale by a large margin to make for comfortable reading -This mode is not automatically activated anywhere because I only rarely need it. -

- -
-
-(use-package darkroom
-  :init
-  (setq darkroom-text-scale-increase 3))
-
-
-
-
-
- -
-

5.6. Ripgrep

-
-

-This is the ripgrep command for Emacs. -

- -
-
-(use-package rg)
-
-
-
-
-
-
-

5.7. Tree-sitter

-
-

-Tree-sitter is a parsing library integrated into Emacs to provide better syntax highlighting and code analysis. It generates concrete syntax trees for source code, enabling more accurate and efficient text processing. Emacs' tree-sitter integration enhances language support, offering features like incremental parsing and precise syntax-aware editing. This improves the development experience by providing robust and dynamic syntax features, making it easier for me to navigate and manipulate code. -

- -

-In order to update the language grammars, run the next command below. -

- -
-
-(mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
-
-
-
- -
-
-(use-package emacs
-  :ensure nil
-  :init
-  (setq treesit-language-source-alist
-        '((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
-          (c . ("https://github.com/tree-sitter/tree-sitter-c"))
-          (cmake . ("https://github.com/uyha/tree-sitter-cmake"))
-          (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
-          (css . ("https://github.com/tree-sitter/tree-sitter-css"))
-          (elisp . ("https://github.com/Wilfred/tree-sitter-elisp"))
-          (go . ("https://github.com/tree-sitter/tree-sitter-go"))
-          (html . ("https://github.com/tree-sitter/tree-sitter-html"))
-          (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
-          (json . ("https://github.com/tree-sitter/tree-sitter-json"))
-          (julia . ("https://github.com/tree-sitter/tree-sitter-julia"))
-          (latex . ("https://github.com/latex-lsp/tree-sitter-latex"))
-          (make . ("https://github.com/alemuller/tree-sitter-make"))
-          (markdown . ("https://github.com/ikatyang/tree-sitter-markdown"))
-          (R . ("https://github.com/r-lib/tree-sitter-r"))
-          (python . ("https://github.com/tree-sitter/tree-sitter-python"))
-          (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "typescript/src" "typescript"))
-          (rust . ("https://github.com/tree-sitter/tree-sitter-rust"))
-          (sql . ("https://github.com/m-novikov/tree-sitter-sql"))
-          (toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
-          (tsx  . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src"))
-          (yaml . ("https://github.com/ikatyang/tree-sitter-yaml"))))
-  )
-
-(use-package treesit-auto
-  :config
-  (global-treesit-auto-mode)
-  (setq treesit-auto-install 'prompt))
-
-
-
-
-
-
- -
-

5.8. direnv

-
-
-
-(use-package direnv
-  :custom (direnv-always-show-summary nil)
-  :config (direnv-mode))
-
-
-
-
-
- -
-

5.9. avy

-
-

-avy provides the ability to search for any character on the screen (not only in the current buffer!) - I enjoy this utility a lot and use it possibly even more often than the native vim commands. -

- -
-
-(use-package avy
-  :bind
-  (("M-o" . avy-goto-char-timer))
-  :config
-  (setq avy-all-windows 'all-frames))
-
-
-
-
-
- -
-

5.10. crdt (Collaborative Editing)

-
-

-With this it is possible to work on the same file collaboratively. I have never tried it out, but it sounds cool. -

- -
-
-(use-package crdt)
-
-
-
-
-
- -
-

5.11. devdocs

-
-

-devdocs is a very nice package that provides documentation from https:devdocs.io. This is very useful since e.g. pyright provides only a very bad documentation and I do not want to leave Emacs all the time just to read documentation. -

- -

-To install a documentation, use the devdocs=install command and select the appropriate version. devdocs-update-all can be used to download and reinstall all installed documents if a newer version is available. Check documentation with devdocs-lookup (C-SPC h d). -

- -
-
-(use-package devdocs)
-
-(add-hook 'python-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1"))))
-(add-hook 'python-ts-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1"))))
-
-(add-hook 'c-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("c"))))
-(add-hook 'c-ts-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("c"))))
-
-(add-hook 'c++-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("cpp"))))
-(add-hook 'c++-ts-mode-hook
-        (lambda () (setq-local devdocs-current-docs '("cpp"))))
-
-(devdocs-update-all)
-
-
-
-
-
- -
-

5.12. Projectile

-
-

-projectile is useful for keeping track of your git projects within Emacs. I mostly use it to quickly switch between projects. -

- -
-
-(use-package projectile
-  :diminish projectile-mode
-  :config (projectile-mode)
-  :custom ((projectile-completion-system 'auto)) ;; integrate ivy into completion system
-  :bind-keymap
-  ("C-c p" . projectile-command-map) ; all projectile commands under this
-  :init
-  ;; NOTE: Set this to the folder where you keep your Git repos!
-  (when (file-directory-p swarsel-projects-directory)
-    (setq projectile-project-search-path (list swarsel-projects-directory)))
-(setq projectile-switch-project-action #'magit-status))
-
-
-
-
-
- -
-

5.13. Magit

-
-

-magit is the best git utility I have ever used - it has a beautiful interface and is very verbose. Here I mostly just setup the list of repositories that I want to expost to magit. -

- -

-Also, Emacs needs a little extra love to accept my Yubikey for git commits etc. We also set that here. -

- -
-
-(use-package magit
-  :config
-  (setq magit-repository-directories `((,swarsel-projects-directory  . 1)
-                                       (,swarsel-emacs-directory . 0)
-                                       (,swarsel-obsidian-directory . 0)
-                                       ("~/.dotfiles/" . 0)))
-  :custom
-  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ; stay in the same window
-
-;; yubikey support for pushing commits
-;; commiting is enabled through nixos gpg-agent config
-(setq epg-pinentry-mode 'loopback)
-(setenv "SSH_AUTH_SOCK" (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))
-
-
-
-
- -
-

5.14. Forge

-
-

-NOTE: Make sure to configure a GitHub token before using this package! -

- - -
-
-(use-package forge
-  :after magit)
-
-(with-eval-after-load 'forge
-  (add-to-list 'forge-alist
-               '("sgit.iue.tuwien.ac.at"
-                 "sgit.iue.tuwien.ac.at/api/v1"
-                 "sgit.iue.tuwien.ac.at"
-                 forge-gitea-repository)))
-
-
-
-
- -
-

5.15. git-timemachine

-
-

-This is just a nice utility to browse different versions of a file of a git project within Emacs. -

- -
-
-(use-package git-timemachine
-   :hook (git-time-machine-mode . evil-normalize-keymaps)
-   :init (setq git-timemachine-show-minibuffer-details t))
-
-
-
-
-
- -
-

5.16. Delimiters (brackets): rainbow-delimiters, highlight-parentheses

-
-
    -
  • rainbow-delimiters colors all delimiters, also ones not in current selection
  • -
  • paren highlights the current delimiter selection especially bold
  • -
  • highlight-parentheses boldly highlights all delimiters in current selection
  • -
- -

-I am not completely sure on electric-pair-mode yet, sometimes it is very helpful, sometimes it annoys me to no end. -

- -
-
-  (use-package rainbow-delimiters
-    :hook (prog-mode . rainbow-delimiters-mode))
-
-  (use-package highlight-parentheses
-    :config
-    (setq highlight-parentheses-colors '("black" "white" "black" "black" "black" "black" "black"))
-    (setq highlight-parentheses-background-colors '("magenta" "blue" "cyan" "green" "yellow" "orange" "red"))
-    (global-highlight-parentheses-mode t))
-
-  (electric-pair-mode 1)
-  (setq electric-pair-preserve-balance nil)
-  ;; don't try to be overly smart
-  (setq electric-pair-delete-adjacent-pairs nil)
-  ;; don't skip newline when auto-pairing parenthesis
-  (setq electric-pair-skip-whitespace-chars '(9 32))
-
-  ;; in org-mode buffers, do not pair < and > in order not to interfere with org-tempo
-(add-hook 'org-mode-hook (lambda ()
-           (setq-local electric-pair-inhibit-predicate
-                   `(lambda (c)
-                  (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c))))))
-
-
-
-
-
- -
-

5.17. rainbow-mode

-
-

-Complimentary to the delimiters-packages above, this package sets the background color of the delimiters, which makes it easier to see at a glance where we are in a delimiter-tree. -

- -
-
-(use-package rainbow-mode
-  :config (rainbow-mode))
-
-
-
-
-
-
-

5.18. Corfu

-
-

-This is the company equivalent to the vertico gang. -I dislike the standard behaviour that makes the cursor move into the completion framework on presses of <up> and <down>. -

- -

-Nerd icons is originally enabled here: Icons -

- -

-Navigation functions defined here: corfu: Do not interrupt navigation -

- -
-
-;; (use-package corfu
-;;   :custom
-;;   (corfu-cycle t)
-;;   :init
-;;   (global-corfu-mode))
-
-(use-package corfu
-  :init
-  (global-corfu-mode)
-  (corfu-history-mode)
-  (corfu-popupinfo-mode) ; Popup completion info
-  :custom
-  (corfu-auto t)
-  (corfu-auto-prefix 3)
-  (corfu-auto-delay 0.3)
-  (corfu-cycle t)
-  (corfu-quit-no-match 'separator)
-  (corfu-separator ?\s)
-  ;; (corfu-quit-no-match t)
-  (corfu-popupinfo-max-height 70)
-  (corfu-popupinfo-delay '(0.5 . 0.2))
-  ;; (corfu-preview-current 'insert) ; insert previewed candidate
-  (corfu-preselect 'prompt)
-  (corfu-on-exact-match nil)      ; Don't auto expand tempel snippets
-  ;; Optionally use TAB for cycling, default is `corfu-complete'.
-  :bind (:map corfu-map
-              ("M-SPC"      . corfu-insert-separator)
-              ("<return>" . swarsel/corfu-normal-return)
-              ;; ("C-<return>" . swarsel/corfu-complete)
-              ("S-<up>" . corfu-popupinfo-scroll-down)
-              ("S-<down>" . corfu-popupinfo-scroll-up)
-              ("C-<up>" . corfu-previous)
-              ("C-<down>" . corfu-next)
-              ("<insert-state> <up>"      . swarsel/corfu-quit-and-up)
-              ("<insert-state> <down>"     . swarsel/corfu-quit-and-down))
-  )
-
-(use-package nerd-icons-corfu)
-
-(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)
-
-(setq nerd-icons-corfu-mapping
-      '((array :style "cod" :icon "symbol_array" :face font-lock-type-face)
-        (boolean :style "cod" :icon "symbol_boolean" :face font-lock-builtin-face)
-        ;; ...
-        (t :style "cod" :icon "code" :face font-lock-warning-face)))
-
-
-
-
-
- -
-

5.19. cape

-
-

-cape adds even more completion capabilities by adding a lot of completion logic that is exposed as separate functions. I tried out adding these to the completion-at-points-functions alist, but I felt like it cluttered my suggestions too much. Hence I now just call the respective functions when I need them. For this I setup the C-z keybinding in General evil. -

- -

-I leave the commented out alist extensions here in case I want to try them out at some point in the future. -

- -
-
-(use-package cape
-  :bind
-  ("C-z p" . completion-at-point) ;; capf
-  ("C-z t" . complete-tag)        ;; etags
-  ("C-z d" . cape-dabbrev)        ;; or dabbrev-completion
-  ("C-z h" . cape-history)
-  ("C-z f" . cape-file)
-  ("C-z k" . cape-keyword)
-  ("C-z s" . cape-elisp-symbol)
-  ("C-z e" . cape-elisp-block)
-  ("C-z a" . cape-abbrev)
-  ("C-z l" . cape-line)
-  ("C-z w" . cape-dict)
-  ("C-z :" . cape-emoji)
-  ("C-z \\" . cape-tex)
-  ("C-z _" . cape-tex)
-  ("C-z ^" . cape-tex)
-  ("C-z &" . cape-sgml)
-  ("C-z r" . cape-rfc1345)
-  ;; Add to the global default value of `completion-at-point-functions' which is
-  ;; used by `completion-at-point'.  The order of the functions matters, the
-  ;; first function returning a result wins.  Note that the list of buffer-local
-  ;; completion functions takes precedence over the global list.
-  ;; (add-to-list 'completion-at-point-functions #'cape-dabbrev)
-  ;; (add-to-list 'completion-at-point-functions #'cape-file)
-  ;; (add-to-list 'completion-at-point-functions #'cape-elisp-block)
-  ;; (add-to-list 'completion-at-point-functions #'cape-history)
-  ;; (add-to-list 'completion-at-point-functions #'cape-keyword)
-  ;; (add-to-list 'completion-at-point-functions #'cape-tex)
-  ;; (add-to-list 'completion-at-point-functions #'cape-sgml)
-  ;; (add-to-list 'completion-at-point-functions #'cape-rfc1345)
-  ;; (add-to-list 'completion-at-point-functions #'cape-abbrev)
-  ;; (add-to-list 'completion-at-point-functions #'cape-dict)
-  ;; (add-to-list 'completion-at-point-functions #'cape-elisp-symbol)
-  ;; (add-to-list 'completion-at-point-functions #'cape-line)
-)
-
-
-
-
-
- -
-

5.20. rust

-
-

-This is supposed to setup a rust-lsp - however, this has not worked nicely in the past and this configuration section is just a ruin really. I need to check what works and clean this up. -

- -
-
-  ;; (use-package rustic
-  ;;   :ensure
-  ;;   :bind (:map rustic-mode-map
-  ;;               ("M-j" . lsp-ui-imenu)
-  ;;               ("M-?" . lsp-find-references)
-  ;;               ("C-c C-c l" . flycheck-list-errors)
-  ;;               ("C-c C-c a" . lsp-execute-code-action)
-  ;;               ("C-c C-c r" . lsp-rename)
-  ;;               ("C-c C-c q" . lsp-workspace-restart)
-  ;;               ("C-c C-c Q" . lsp-workspace-shutdown)
-  ;;               ("C-c C-c s" . lsp-rust-analyzer-status))
-  ;;   :config
-
-  ;;   (setq rustic-format-on-save t)
-  ;;   (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))
-
-;;   (defun rk/rustic-mode-hook ()
-;;     ;; so that run C-c C-c C-r works without having to confirm, but don't try to
-;;     ;; save rust buffers that are not file visiting. Once
-;;     ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should
-;;     ;; no longer be necessary.
-;;     (when buffer-file-name
-;;       (setq-local buffer-save-without-query t))
-;;     (add-hook 'before-save-hook 'lsp-format-buffer nil t))
-
-;; (use-package rustic
-;;   :config
-;;   (setq rustic-format-on-save t)
-;;   (setq rustic-lsp-client 'eglot)
-;;   :custom
-;;   (lsp-rust-analyzer-cargo-watch-command "clippy")
-;;   (lsp-rust-analyzer-server-display-inlay-hints t)
-;;   :mode ("\\.rs" . rustic-mode))
-
-(use-package rustic
-  :config
-  (setq rustic-format-on-save t)
-  (setq rustic-lsp-client 'eglot)
-  :mode ("\\.rs" . rustic-mode))
-
-
-
-
- -
-

5.21. Tramp

-
-

-Tramp allows for SSH access of files over Emacs. I have no ideas what the options here mean, but this is a recommended configuration that I found (sadly I lost the link). I need to research more what these options really do. -

- -
-
-
-
-(use-package tramp
-  :init
-  (setq vc-ignore-dir-regexp
-        (format "\\(%s\\)\\|\\(%s\\)"
-                vc-ignore-dir-regexp
-                tramp-file-name-regexp))
-  (setq tramp-default-method "ssh")
-  (setq tramp-auto-save-directory
-        (expand-file-name "tramp-auto-save" user-emacs-directory))
-  (setq tramp-persistency-file-name
-        (expand-file-name "tramp-connection-history" user-emacs-directory))
-  (setq password-cache-expiry nil)
-  (setq tramp-use-ssh-controlmaster-options nil)
-  (setq remote-file-name-inhibit-cache nil)
-  :config
-  (customize-set-variable 'tramp-ssh-controlmaster-options
-                          (concat
-                           "-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p "
-                           "-o ControlMaster=auto -o ControlPersist=yes"))
-)
-
-
-
-
-
-
-
- -
-

5.22. diff-hl

-
-

-This is a simple highlighting utility that uses the margin to visually show the differences since the last git commit. -

- -
-
-(use-package diff-hl
-  :hook
-  ((prog-mode
-    org-mode) . diff-hl-mode)
-  :init
-  (diff-hl-flydiff-mode)
-  (diff-hl-margin-mode)
-  (diff-hl-show-hunk-mouse-mode))
-
-
-
-
-
-
-

5.23. Commenting

-
-

-This package allows for swift commenting out and in of code snippets. For some reason, it is a bit broken in my config, as it sometimes comments out too much, sometimes too little, and sometimes it splits lines during commenting. Also, in org-mode when inside a src-block, it often times jumps to the top of the block. -

- -

-Still, this is avery convenient package. -

- -
-
-(use-package evil-nerd-commenter
-  :bind ("M-/" . evilnc-comment-or-uncomment-lines))
-
-
-
-
-
- -
-

5.24. yasnippet

-
-

-yasnippet allows to define snippets that can be quickly expanded by hitting the TAB key after inputting a keyword. -

- -

-I used to run this together with the yasnippet-snippets package, but the snippets in there I did not find all too useful for myself. I need to create some custom snippets here one day. -

- -
-
-(use-package yasnippet
-  :init (yas-global-mode 1)
-  :config
-  (yas-reload-all))
-
-
-
-
- -
    -
  1. yasnippet math-snippets
    -
    -

    -The following block is mostly inspired from https://code.kulupu.party/thesuess/WTFmacs/ and sets up a few prefixes that make LaTeX-math-mode nicer to use even with auctex and cape enabled. -

    - -
    -
    -
    -(setq wtf/latex-mathbb-prefix "''")
    -(setq swarsel/latex-mathcal-prefix "``")
    -
    -(use-package yasnippet
    -  :config
    -
    -  (setq wtf/english-alphabet
    -        '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"))
    -
    -  (dolist (elem wtf/english-alphabet)
    -    (when (string-equal elem (downcase elem))
    -      (add-to-list 'wtf/english-alphabet (upcase elem))))
    -
    -
    -  (yas-define-snippets
    -   'latex-mode
    -   (mapcar
    -    (lambda (elem)
    -      (list (concat wtf/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
    -    wtf/english-alphabet))
    -
    -  (yas-define-snippets
    -   'latex-mode
    -   (mapcar
    -    (lambda (elem)
    -      (list (concat swarsel/latex-mathcal-prefix elem) (concat "\\mathcal{" elem "}") (concat "Mathcal letter " elem)))
    -    wtf/english-alphabet))
    -
    -  (setq swtf/latex-math-symbols
    -        '(("x" . "\\times")
    -          ("*" . "\\cdot")
    -          ("." . "\\ldots")
    -          ("op" . "\\operatorname{$1}$0")
    -          ("o" . "\\circ")
    -          ("V" . "\\forall")
    -          ("v" . "\\vee")
    -          ("w" . "\\wedge")
    -          ("q" . "\\quad")
    -          ("f" . "\\frac{$1}{$2}$0")
    -          ("s" . "\\sum_{$1}^{$2}$0")
    -          ("p" . "\\prod_{$1}^{$2}$0")
    -          ("e" . "\\exists")
    -          ("i" . "\\int_{$1}^{$2}$0")
    -          ("c" . "\\cap")
    -          ("u" . "\\cup")
    -          ("0" . "\\emptyset")))
    -
    -  )
    -
    -
    -
    -
    -
    -
  2. -
-
- -
-

5.25. eglot

-
-

-After having tried out lsp-mode and lsp-bridge for a while each, I must say that eglot feels the most clean and fast to me. Rust-modes need a little extra care to get working here. -

- -

-:CUSTOMID: h:424fbc62-84e2-42c7-a1ca-e43ea04c43e5 -

- -
-
-(use-package eglot
-  :ensure nil
-  :hook
-  ((python-mode
-    python-ts-mode
-    c-mode
-    c-ts-mode
-    c++-mode
-    c++-ts-mode
-    tex-mode
-    LaTeX-mode
-    ) . (lambda () (progn
-                     (eglot-ensure)
-                     (add-hook 'before-save-hook 'eglot-format nil 'local))))
-  :custom
-  (eldoc-echo-area-use-multiline-p nil)
-  (completion-category-defaults nil)
-  :config
-  ;; (push '(rustic-ts-mode . eglot-rust-analyzer) eglot-server-programs)
-  (push '(rustic-mode . eglot-rust-analyzer) eglot-server-programs)
-  (add-to-list 'eglot-server-programs '((rust-mode) . (eglot-rust-analyzer "rust-analyzer")))
-  ;; (add-to-list 'eglot-server-programs '((python-mode) . ("pylsp")))
-  ;; (add-to-list 'eglot-server-programs '((c-mode) . ("clangd")))
-  :bind (:map eglot-mode-map
-              ("M-(" . flymake-goto-next-error)
-              ("C-c ," . eglot-code-actions)))
-
-(defalias 'start-lsp-server #'eglot)
-
-
-
-
-
- -
-

5.26. Breadcrumb

-
-

-This simple shows the path to the current file on the top of the buffer - I just think it looks kind of neat, even though it is not extremely useful :) -

- -
-
-(use-package breadcrumb
-  :config (breadcrumb-mode))
-
-
-
-
-
- -
-

5.27. Prevent breaking of hardlinks

-
-

-This setting ensures that hard links are preserved during the backup process, which is useful for maintaining the integrity of files that are linked in multiple locations. -

- -
-
-(setq backup-by-copying-when-linked t)
-
-
-
-
-
- -
-

5.28. Dirvish

-
-

-Dirvish is an improvement upon the dired-framework and has more features like file preview etc. Sadly it has an incompatibility with openwith which is why I have disabled that package. -

- -
-
-(use-package dirvish
-  :init
-  (dirvish-override-dired-mode)
-  :config
-  (dirvish-peek-mode)
-  (dirvish-side-follow-mode)
-  (setq dirvish-open-with-programs
-        (append dirvish-open-with-programs '(
-                                             (("xlsx" "docx" "doc" "odt" "ods") "libreoffice" "%f")
-                                             (("jpg" "jpeg" "png")              "imv" "%f")
-                                             (("pdf")                           "sioyek" "%f")
-                                             (("xopp")                          "xournalpp" "%f"))))
-  :custom
-  (delete-by-moving-to-trash t)
-  (dired-listing-switches
-   "-l --almost-all --human-readable --group-directories-first --no-group")
-  (dirvish-attributes
-   '(vc-state subtree-state nerd-icons collapse file-time file-size))
-  (dirvish-quick-access-entries
-   '(("h" "~/"              "Home")
-     ("c" "~/.dotfiles/"    "Config")
-     ("d" "~/Downloads/"    "Downloads")
-     ("D" "~/Documents/"    "Documents")
-     ("p" "~/Documents/GitHub/"  "Projects")
-     ("/" "/"               "Root")))
-  :bind
-  (("<DUMMY-i> d" . 'dirvish)
-   ("C-=" . 'dirvish-side)
-   :map dirvish-mode-map
-   ("h"   . dired-up-directory)
-   ("<left>"   . dired-up-directory)
-   ("l"   . dired-find-file)
-   ("<right>"   . dired-find-file)
-   ("j"   . evil-next-visual-line)
-   ("k"   . evil-previous-visual-line)
-   ("a"   . dirvish-quick-access)
-   ("f"   . dirvish-file-info-menu)
-   ("z"   . dirvish-history-last)
-   ("J"   . dirvish-history-jump)
-   ("y"   . dirvish-yank-menu)
-   ("/"   . dirvish-narrow)
-   ("TAB" . dirvish-subtree-toggle)
-   ("M-f" . dirvish-history-go-forward)
-   ("M-b" . dirvish-history-go-backward)
-   ("M-l" . dirvish-ls-switches-menu)
-   ("M-m" . dirvish-mark-menu)
-   ("M-t" . dirvish-layout-toggle)
-   ("M-s" . dirvish-setup-menu)
-   ("M-e" . dirvish-emerge-menu)
-   ("M-j" . dirvish-fd-jump)))
-
-
-
-
-
-
-

5.29. pdf-tools: pdf-viewer and support for dirvish

-
-

-This enables pdf-previewing in dirvish and gives a much better pdf-viewer than is shipped normally by emacs. -

- -
-
-(use-package pdf-tools
-  :init
-  (if (not (boundp 'pdf-tools-directory))
-      (pdf-tools-install))
-  :mode ("\\.pdf" . pdf-view-mode))
-
-
-
-
-
- -
-

5.30. Jupyter

-
-

-This is a jupyter client. Using it is a bit cumbersome though, so I have not fully explored all features. -

- -
-
-(use-package ein)
-
-
-
-
-
- -
-

5.31. undo-tree

-
-

-Base emacs undo logic is very useful, but not easy to understand for me. I prefer undo-tree, which makes switching between branches easier and also allows quickly switching back to a much older state using the visualizer. -

- -

-Evil needs to be told to use this mode, see (evil-set-undo-system 'undo-tree) in Evil/General. -

- -

-By default, I am not using undo-tree-mode in every buffer. This might change in the future, but for now this is fine. It can be enabled manually should the need arise. -

- -

-While we are at it, we are also setting up a persistent undo-file for every file that we are working with. -

- -
-
-(use-package undo-tree
-  ;; :init (global-undo-tree-mode)
-  :bind (:map undo-tree-visualizer-mode-map
-              ("h" . undo-tree-visualize-switch-branch-left)
-              ("l" . undo-tree-visualize-switch-branch-left)
-              ("j" . undo-tree-visualize-redo)
-              ("k" . undo-tree-visualize-undo))
-  :config
-  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))
-
-(add-hook 'prog-mode-hook 'undo-tree-mode)
-(add-hook 'text-mode-hook 'undo-tree-mode)
-(add-hook 'org-mode-hook 'undo-tree-mode)
-(add-hook 'latex-mode-hook 'undo-tree-mode)
-
-
-
-
-
-

5.32. Hydra

-
-

-Hydra allows for the writing of macro-style functions. I have not yet looked into this all too much, but it seems to be a potent feature. -

- -
-
-(use-package hydra)
-
-
-
-
- -
-

5.32.1. Text scaling

-
-

-I only wrote this in order to try out hydra; rarely do I really need this. However, it can be useful for Presentations. It simply scales the text size. -

- -
-
-
-;; change the text size of the current buffer
-(defhydra hydra-text-scale (:timeout 4)
-  "scale text"
-  ("j" text-scale-increase "in")
-  ("k" text-scale-decrease "out")
-  ("f" nil "finished" :exit t))
-
-
-
-
-
-
-
-

5.33. External Applications

-
-
-
-

5.33.1. Obsidian

-
-

-This provides an interface to Obsidian for Emacs - as much as I want to like it, I actually enjoy using the official Obsidian app more - even though that cannot be used by Emacs directly. -

- -

-My workflow for Obsidian is now as follows: -

- -
    -
  1. create notes either in Emacs or Obsidian
  2. -
  3. -look at them in the official client -

    - -

    -I hope that this package will improve, then I will come back to it one day. -

  4. -
- -
-
-;; (use-package obsidian
-;;   :ensure t
-;;   :demand t
-;;   :config
-;;   (obsidian-specify-path swarsel-obsidian-vault-directory)
-;;   (global-obsidian-mode t)
-;;   :custom
-;;   ;; This directory will be used for `obsidian-capture' if set.
-;;   (obsidian-inbox-directory "Inbox")
-;;   (bind-key (kbd "C-c M-o") 'obsidian-hydra/body 'obsidian-mode-map)
-;;   :bind (:map obsidian-mode-map
-;;               ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
-;;               ("C-c C-o" . obsidian-follow-link-at-point)
-;;               ;; Jump to backlinks
-;;               ("C-c C-b" . obsidian-backlink-jump)
-;;               ;; If you prefer you can use `obsidian-insert-link'
-;;               ("C-c C-l" . obsidian-insert-wikilink)))
-
-
-
-
-
- -
-

5.33.2. Anki

-
-

-This section is here to make Anki usable from within Emacs - an endeavour that I have mostly given up on. -

-
- -
    -
  1. Basic Anki setup
    -
    -
    -
    -;; (use-package anki-editor
    -;;   :after org
    -;;   :bind (:map org-mode-map
    -;;               ("<f12>" . anki-editor-cloze-region-auto-incr)
    -;;               ("<f11>" . anki-editor-cloze-region-dont-incr)
    -;;               ("<f10>" . anki-editor-reset-cloze-number)
    -;;               ("<f9>"  . anki-editor-push-tree))
    -;;   :hook (org-capture-after-finalize . anki-editor-reset-cloze-number) ; Reset cloze-number after each capture.
    -;;   :config
    -;;   (setq anki-editor-create-decks t ;; Allow anki-editor to create a new deck if it doesn't exist
    -;;         anki-editor-org-tags-as-anki-tags t)
    -
    -;;   (defun anki-editor-cloze-region-auto-incr (&optional arg)
    -;;     "Cloze region without hint and increase card number."
    -;;     (interactive)
    -;;     (anki-editor-cloze-region swarsel-anki-editor-cloze-number "")
    -;;     (setq swarsel-anki-editor-cloze-number (1+ swarsel-anki-editor-cloze-number))
    -;;     (forward-sexp))
    -;;   (defun anki-editor-cloze-region-dont-incr (&optional arg)
    -;;     "Cloze region without hint using the previous card number."
    -;;     (interactive)
    -;;     (anki-editor-cloze-region (1- swarsel-anki-editor-cloze-number) "")
    -;;     (forward-sexp))
    -;;   (defun anki-editor-reset-cloze-number (&optional arg)
    -;;     "Reset cloze number to ARG or 1"
    -;;     (interactive)
    -;;     (setq swarsel-anki-editor-cloze-number (or arg 1)))
    -;;   (defun anki-editor-push-tree ()
    -;;     "Push all notes under a tree."
    -;;     (interactive)
    -;;     (anki-editor-push-notes '(4))
    -;;     (anki-editor-reset-cloze-number))
    -;;   ;; Initialize
    -;;   (anki-editor-reset-cloze-number)
    -;;   )
    -
    -;; (require 'anki-editor)
    -
    -
    -
    -
    -
  2. - -
  3. Own Anki functions
    -
    -
      -
    • These functions enable you to quickly set the destination note type and deck
    • -
    - -
    -
    -;; (defvar swarsel-anki-deck nil)
    -;; (defvar swarsel-anki-notetype nil)
    -;; (defvar swarsel-anki-fields nil)
    -
    -;; (defun swarsel-anki-set-deck-and-notetype ()
    -;;   (interactive)
    -;;   (setq swarsel-anki-deck  (completing-read "Choose a deck: "
    -;;                                             (sort (anki-editor-deck-names) #'string-lessp)))
    -;;   (setq swarsel-anki-notetype (completing-read "Choose a note type: "
    -;;                                                (sort (anki-editor-note-types) #'string-lessp)))
    -;;   (setq swarsel-anki-fields (progn
    -;;                               (anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,swarsel-anki-notetype)))))
    -;;   )
    -
    -;; (defun swarsel-anki-make-template-string ()
    -;;   (if (not swarsel-anki-deck)
    -;;       (call-interactively 'swarsel-anki-set-deck-and-notetype))
    -;;   (setq swarsel-temp swarsel-anki-fields)
    -;;   (concat (concat "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: " swarsel-anki-notetype "\n:ANKI_DECK: " swarsel-anki-deck "\n:END:\n** ")(pop swarsel-temp) "\n%?\n** " (mapconcat 'identity swarsel-temp "\n\n** ") "\n\n"))
    -
    -;; (defun swarsel-today()
    -;;   (format-time-string "%Y-%m-%d"))
    -
    -;; (defun swarsel-obsidian-daily ()
    -;;   (interactive)
    -;;   (if (not (file-exists-p (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
    -;;       (write-region "" nil (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))
    -;;     )
    -;;   (find-file (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
    -
    -
    -
    -
    -
  4. -
-
-
- -
-

5.34. Email + Calendar

-
-
-
-

5.34.1. make sure mu4e is found

-
-

-This seems not to be needed - I do not yet dare to delete it though. -

- -
-
-;; (let ((mu4epath
-;;        (concat
-;;         (f-dirname
-;;          (file-truename
-;;           (executable-find "mu")))
-;;         "/../share/emacs/site-lisp/mu4e")))
-;;   (when (and
-;;          (string-prefix-p "/nix/store/" mu4epath)
-;;          (file-directory-p mu4epath))
-;;     (add-to-list 'load-path mu4epath)))
-
-
-
-
-
- -
-

5.34.2. mu4e

-
-

-In this section we are setting up mu4e, a mail client for emacs using mu with mbsync as backend. The mail accounts themselves are setup in the NixOS configuration, so we only need to add Emacs specific settings here. -

- -

-The hook functions are defined here: mu4e functions -

- -
-
-(use-package mu4e
-  :ensure nil
-  ;; :load-path "/usr/share/emacs/site-lisp/mu4e/"
-  ;;:defer 20 ; Wait until 20 seconds after startup
-  :config
-
-  ;; This is set to 't' to avoid mail syncing issues when using mbsync
-  (setq send-mail-function 'sendmail-send-it)
-  (setq mu4e-change-filenames-when-moving t)
-  (setq mu4e-mu-binary (executable-find "mu"))
-  (setq mu4e-hide-index-messages t)
-
-  (setq mu4e-update-interval 180)
-  (setq mu4e-get-mail-command "mbsync -a")
-  (setq mu4e-maildir "~/Mail")
-
-  ;; enable inline images
-  (setq mu4e-view-show-images t)
-  ;; use imagemagick, if available
-  (when (fboundp 'imagemagick-register-types)
-    (imagemagick-register-types))
-
-  (setq mu4e-drafts-folder "/Drafts")
-  (setq mu4e-sent-folder   "/Sent Mail")
-  (setq mu4e-refile-folder "/All Mail")
-  (setq mu4e-trash-folder  "/Trash")
-
-  (setq mu4e-maildir-shortcuts
-        '((:maildir "/leon/Inbox"    :key ?1)
-          (:maildir "/nautilus/Inbox" :key ?2)
-          (:maildir "/mrswarsel/Inbox"     :key ?3)
-          (:maildir "/Sent Mail"     :key ?s)
-          (:maildir "/Trash"     :key ?t)
-          (:maildir "/Drafts"     :key ?d)
-          (:maildir "/All Mail"     :key ?a)))
-
-(setq user-mail-address "leon@swarsel.win"
-      user-full-name "Leon Schwarzäugl")
-
-
-(setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com)))
-
-
-(add-hook 'mu4e-compose-mode-hook #'swarsel/mu4e-send-from-correct-address)
-(add-hook 'mu4e-compose-post-hook #'swarsel/mu4e-restore-default)
-
-
-
-
- -
-

5.34.3. mu4e-alert

-
-

-This adds the simple utility of sending desktop notifications whenever a new mail is received. I am using libnotify because I want to use this with notify-send. -

- -
-
-(use-package mu4e-alert
-:config
-(setq mu4e-alert-set-default-style 'libnotify))
-
-(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
-
-(mu4e t)
-
-
-
-
- -
-

5.34.4. Calendar

-
-

-This provides a beautiful calender to emacs. -

- -

-Yes, I am aware that I am exposing my university-calendar to the public here. I can imagine worse things ;) if you however know how to obscure this, let me know! -

- -
-
-(use-package org-caldav
-  :init
-  ;; set org-caldav-sync-initalization
-  (setq swarsel-caldav-synced 0)
-  (setq org-caldav-url "https://stash.swarsel.win/remote.php/dav/calendars/Swarsele")
-  (setq org-caldav-calendars
-        '((:calendar-id "personal"
-                        :inbox "~/Calendars/leon_cal.org")))
-  ;; (setq org-caldav-backup-file "~/org-caldav/org-caldav-backup.org")
-  ;; (setq org-caldav-save-directory "~/org-caldav/")
-
-  :config
-  (setq org-icalendar-alarm-time 1)
-  ;; This makes sure to-do items as a category can show up on the calendar
-  (setq org-icalendar-include-todo t)
-  ;; This ensures all org "deadlines" show up, and show up as due dates
-  (setq org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due))
-  ;; This ensures "scheduled" org items show up, and show up as start times
-  (setq org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo))
-  )
-
-(use-package calfw
-  :ensure nil
-  :bind ("C-c A" . swarsel/open-calendar)
-  :init
-  (use-package calfw-cal
-    :ensure nil)
-  (use-package calfw-org
-    :ensure nil)
-  (use-package calfw-ical
-    :ensure nil)
-  :config
-  (bind-key "g" 'cfw:refresh-calendar-buffer cfw:calendar-mode-map)
-  (bind-key "q" 'evil-quit cfw:details-mode-map)
-  ;; (custom-set-faces
-  ;;  '(cfw:face-title ((t (:foreground "#f0dfaf" :weight bold :height 65))))
-  ;; )
-  )
-
-(defun swarsel/open-calendar ()
-  (interactive)
-  (unless (eq swarsel-caldav-synced 1) (org-caldav-sync) (setq swarsel-caldav-synced 1))
-  ;;  (select-frame (make-frame '((name . "calendar")))) ; makes a new frame and selects it
-  ;; (set-face-attribute 'default (selected-frame) :height 65) ; reduces the font size of the new frame
-  (cfw:open-calendar-buffer
-   :contents-sources
-   (list
-    (cfw:org-create-source "Purple")  ; orgmode source
-    (cfw:ical-create-source "TISS" "https://tiss.tuwien.ac.at/events/rest/calendar/personal?locale=de&token=4463bf7a-87a3-490a-b54c-99b4a65192f3" "Cyan"))))
-
-
-
-
-
-
- -
-

5.35. Dashboard: emacs startup screen

-
-

-This sets up the dashboard, which is really quite useless. But, it looks cool and makes me happy whenever I start an emacsclient without a file name as argument :) -

- -
-
-(use-package dashboard
-  :ensure t
-  :config
-  (dashboard-setup-startup-hook)
-  ;; (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
-  (setq dashboard-display-icons-p t ;; display icons on both GUI and terminal
-        dashboard-icon-type 'nerd-icons ;; use `nerd-icons' package
-        dashboard-set-file-icons t
-        dashboard-items '((recents . 5)
-                          (projects . 5)
-                          (agenda . 5))
-        dashboard-set-footer nil
-        dashboard-banner-logo-title "Welcome to SwarsEmacs!"
-        dashboard-image-banner-max-height 300
-        dashboard-startup-banner "~/.dotfiles/wallpaper/swarsel.png"
-        dashboard-projects-backend 'projectile
-        dashboard-projects-switch-function 'magit-status
-        dashboard-set-navigator t
-        dashboard-startupify-list '(dashboard-insert-banner
-                                    dashboard-insert-newline
-                                    dashboard-insert-banner-title
-                                    dashboard-insert-newline
-                                    dashboard-insert-navigator
-                                    dashboard-insert-newline
-                                    dashboard-insert-init-info
-                                    dashboard-insert-items
-                                    )
-        dashboard-navigator-buttons
-        `(;; line1
-          ((,""
-            "SwarselSocial"
-            "Browse Swarsele"
-            (lambda (&rest _) (browse-url "instagram.com/Swarsele")))
-
-           (,""
-            "SwarselSound"
-            "Browse SwarselSound"
-            (lambda (&rest _) (browse-url "sound.swarsel.win")) )
-           (,""
-            "SwarselSwarsel"
-            "Browse Swarsel"
-            (lambda (&rest _) (browse-url "github.com/Swarsel")) )
-           (,""
-            "SwarselStash"
-            "Browse SwarselStash"
-            (lambda (&rest _) (browse-url "stash.swarsel.win")) )
-           (,"󰫑"
-            "SwarselSport"
-            "Browse SwarselSports"
-            (lambda (&rest _) (browse-url "social.parkour.wien/@Lenno")))
-           )
-          (
-           (,"󱄅"
-            "swarsel.win"
-            "Browse swarsel.win"
-            (lambda (&rest _) (browse-url "swarsel.win")))
-           )
-          )))
-
-
-
-
-
-
-
-
-
-

Author: Leon Schwarzäugl

-

Created: 2024-06-20 Do 01:55

-

Validate

-
- - diff --git a/Emacs.org b/Emacs.org deleted file mode 100644 index 53a4d07..0000000 --- a/Emacs.org +++ /dev/null @@ -1,3243 +0,0 @@ -#+title: Emacs Configuration -#+PROPERTY: header-args:emacs-lisp :tangle programs/emacs/init.el :mkdirp yes -#+macro: revision-date (eval (format-time-string "%F %T %z")) -#+macro: count-words (eval (count-words (point-min) (point-max))) - -*This file has {{{count-words}}} words and was last revised on {{{revision-date}}}.* - -* Introduction (no code) -:PROPERTIES: -:CUSTOM_ID: h:a86fe971-f169-4052-aacf-15e0f267c6cd -:END: - -This is my Emacs literate configuration. It is part of a NixOS system that is fully declarative and can be found here: - -- [[https:github.com/Swarsel/.dotfiles][~SwarselSystems~ on github.com]] -- [[https:swagit.swarsel.win/Swarsel/.dotfiles][~SwarselSystems~ on swagit.swarsel.win]] - -The literate configuration lets me explain my choices to my future self as well as you, the reader. I go to great lengths to explain the choices for all configuration steps that I take in order for me to pay due diligence in crafting my setup, and not simply copying big chunks of other peoples code. Also, the literate configuration approach is very convenient to me as I only need to keep of (ideally) a single file to manage all of my configuration. I hope that this documentation will make it easier for beginners to get into Emacs and NixOS as I know it can be a struggle in the beginning. - -Due to a NixOS specific setting, I currently manage two files (=Emacs.org= and =Nixos.org=), but I hope to soon find a way to consolidate them into a single file. - -My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: {{{revision-date}}}) - -#+begin_src emacs-lisp :tangle no :exports both - - system-configuration-options - -#+end_src - -#+RESULTS: -: --prefix=/nix/store/lymgpfqr5dp1wc0khbcbhhjnxq8ccsy9-emacs-pgtk-20240521.0 --disable-build-details --with-modules --with-pgtk --with-compress-install --with-toolkit-scroll-bars --with-native-compilation --without-imagemagick --with-mailutils --without-small-ja-dic --with-tree-sitter --without-xinput2 --with-xwidgets --with-dbus --with-selinux - -This file is not loaded by Emacs directly as the configuration (even though this would be possible) - instead, it generates two more files: - -- =early-init.el= - This file handle startup optimization and sets up the basic frame that I will be working in. -- =init.el= - This file handles the rest of the Emacs configuration. - -By using the configuration offered by this file, the file you are reading right now (=Emacs.org=) will be freshly tangled on every file save. However, when you clone this configuration yourself and have not yet activated it, you need to tangle the file yourself. This can be done using the keybind =C-c C-v t=. Alternatively, execute the following block: - - #+begin_src emacs-lisp :tangle no :export both :results silent - - (org-babel-tangle) - - #+end_src - -Lastly, you will notice there is no package system setup in this configuration. This is because packages are automatically handled on the NixOS side by parsing the =Emacs.org= file for package installs. -* Initialization (early-init.el) -:PROPERTIES: -:CUSTOM_ID: h:2c331451-45ed-4592-9e00-d36b5bf31248 -:END: - -In this section I handle my early init file; it takes care of frame-setup for emacsclient buffers. - -** Increase startup performance -:PROPERTIES: -:CUSTOM_ID: h:38e03b65-9dfc-4547-b27d-236664d7dc15 -:END: - -First, I use some advice from doomemacs regarding garbace collection; here I make sure that during startup, the garbace collectur will not run, which will improve startup times. Now, that might not really be needed since I will usually only start the emacs server once during startup and then not touch it again, however, since I am building my emacs configuration using NixOS, there is some merit to this since I will usually need to restart the server once I rebuild my configuration. - -Also, inspired by a setting I have seen in protesilaos' configuration, I apply the same idea to the =file-name-handler-alist= and =vc-handled-backends=. - -In the end, we need to restore those values to values that will work during normal operation. For that, I add a hook to the startup function that will revert the values once Emacs has finished initialization. - -Also packed into the hook function is the line =(fset 'epg-wait-for-status 'ignore)=. This line is needed at the end of the configuration in order to allow for my Yubikey to be used to encrypt and decrypt =.gpg= files. Without it, Emacs will just hang forever and basically crash. - -#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes - (defvar swarsel-file-name-handler-alist file-name-handler-alist) - (defvar swarsel-vc-handled-backends vc-handled-backends) - - (setq gc-cons-threshold most-positive-fixnum - gc-cons-percentage 0.6 - file-name-handler-alist nil - vc-handled-backends nil) - - (add-hook 'emacs-startup-hook - (lambda () - (progn - (setq gc-cons-threshold (* 1000 1000 8) - gc-cons-percentage 0.1 - file-name-handler-alist swarsel-file-name-handler-alist - vc-handled-backends swarsel-vc-handled-backends) - (fset 'epg-wait-for-status 'ignore) - ))) - -#+end_src -** Setup frames -:PROPERTIES: -:CUSTOM_ID: h:782b3632-afb2-4c67-8c46-ff94408aef5d -:END: - -Next, I will setup the basic frame for my emacs buffers. Note that I use a tiling window manager, so I do not need to hold myself up with sizing the windows myself. I also disable some GUI tools that I (like many others) do not find to be particularly useful. Also I inhibit many startup functions here, even though it does not affect me greatly since I use another solution for that. - -We also make require immediate compilation of native code. - -For the =default-frame-alist=, I used to also set ='(right-divider-width . 4)= and ='(bottom-divider-width . 4)=, but I did not like the look of the divider bar and usually know my splits anyways, so this is no longer set. - -#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes - (tool-bar-mode 0) - (menu-bar-mode 0) - (scroll-bar-mode 0) - - (setq frame-inhibit-implied-resize t - ring-bell-function 'ignore - use-dialog-box nil - use-file-dialog nil - use-short-answers t - inhibit-startup-message t - inhibit-splash-screen t - inhibit-startup-screen t - inhibit-x-resources t - inhibit-startup-buffer-menu t - inhibit-startup-echo-area-message user-login-name ; this needs to be set to the username or it will not have an effect - comp-deferred-compilation nil ; compile all Elisp to native code immediately - ) - - (setq-default left-margin-width 1 - right-margin-width 1) - - (setq-default default-frame-alist - (append - (list - '(undecorated . t) ; no title bar, borders etc. - '(background-color . "#1D252C") ; load doom-citylight colors to avoid white flash - '(foreground-color . "#A0B3C5") ; load doom-citylight colors to avoid white flash - '(vertical-scroll-bars . nil) - '(horizontal-scroll-bars . nil) - '(internal-border-width . 5) - '(tool-bar-lines . 0) - '(menu-bar-lines . 0)))) - -#+end_src -** Make C-i, C-m, C-[ available in graphic sessions -:PROPERTIES: -:CUSTOM_ID: h:396c47f2-7e2f-4fad-ae71-6483bf7e3e42 -:END: - -By default, emacs binds -- =C-i= to the =TAB= key -- =C-m= to the =RET= key -- =C-[= to the =ECS= key - -These keybinds exist to make Emacs work well in terminal mode. However, most of the time I am using Emacs in a graphic session, and I would hence like to have these keybinds available for personal use. - -NOTE: To use these keybinds, you need to enclose the binding in angled brackets (=<>=). Then they can be used normally - -#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes - - (add-hook - 'after-make-frame-functions - (lambda (frame) - (with-selected-frame frame - (when (display-graphic-p) - (define-key input-decode-map (kbd "C-i") [DUMMY-i]) - (define-key input-decode-map (kbd "C-[") [DUMMY-lsb]) - (define-key input-decode-map (kbd "C-m") [DUMMY-m]) - )))) - - - - -#+end_src -* Personal settings -:PROPERTIES: -:CUSTOM_ID: h:601ba407-b906-4869-8ef6-67a9fc285fba -:END: - -This section is used to define my own functions, own variables, and own keybindings. - -** Custom functions -:PROPERTIES: -:CUSTOM_ID: h:b7b5976a-db2b-493d-8794-1924a0e12aec -:END: - -In this section I define extra functions that I need. Some of these functions I wrote myself, some I found after internet reseach. For functions I found on the internet, I will link the original source I found it in. - -*** Emacs/Evil state toggle -:PROPERTIES: -:CUSTOM_ID: h:b715d7cf-da09-4e0c-95bc-8281b4f3ce9c -:END: - -Since I am rebinding the =C-z= hotkey for emacs-evil-state toggling, I want to have a function that still lets me perform this action quickly. - -#+begin_src emacs-lisp - - (defun swarsel/toggle-evil-state () - (interactive) - (if (or (evil-emacs-state-p) (evil-insert-state-p)) - (evil-normal-state) - (evil-emacs-state))) - -#+end_src - -*** Switching to last used buffer -:PROPERTIES: -:CUSTOM_ID: h:1e0ee570-e509-4ecb-a3af-b75543731bb0 -:END: - -I often find myself bouncing between two buffers when I do not want to use a window split. This funnction simply jumps to the last used buffer. - -#+begin_src emacs-lisp - - (defun swarsel/last-buffer () (interactive) (switch-to-buffer nil)) - -#+end_src -*** mu4e functions -:PROPERTIES: -:CUSTOM_ID: h:34506761-06b9-43b5-a818-506d9b3faf28 -:END: - -I use these functions to let me switch between my main email accounts, as mu4e by itself has trouble doing so. =mu4e-switch-account= allows for manual choosing of the sender account, while =mu4e-rfs--matching-address= and =mu4e-send-from-correct-address= are used when replying to a mail; they switch the sender account to the one that received the mail. - -By default, the sender email will not be changed after sending a mail; however, I want Emacs to always use my main address when not replying to another email. For that I use =mu4e-restore-default=. - -Used here: [[#h:b92a18cf-eec3-4605-a8c2-37133ade3574][mu4e]] - -#+begin_src emacs-lisp - - (defun swarsel/mu4e-switch-account () - (interactive) - (let ((account (completing-read "Select account: " mu4e-user-mail-address-list))) - (setq user-mail-address account))) - - (defun swarsel/mu4e-rfs--matching-address () - (cl-loop for to-data in (mu4e-message-field mu4e-compose-parent-message :to) - for to-email = (pcase to-data - (`(_ . email) email) - (x (mu4e-contact-email x))) - for to-name = (pcase to-data - (`(_ . name) name) - (x (mu4e-contact-name x))) - when (mu4e-user-mail-address-p to-email) - return (list to-name to-email))) - - (defun swarsel/mu4e-send-from-correct-address () - (when mu4e-compose-parent-message - (save-excursion - (when-let ((dest (swarsel/mu4e-rfs--matching-address))) - (cl-destructuring-bind (from-user from-addr) dest - (setq user-mail-address from-addr) - (message-position-on-field "From") - (message-beginning-of-line) - (delete-region (point) (line-end-position)) - (insert (format "%s <%s>" (or from-user user-full-name) from-addr))))))) - - (defun swarsel/mu4e-restore-default () - (setq user-mail-address "leon@swarsel.win" - user-full-name "Leon Schwarzäugl")) - - -#+end_src -*** Create non-existant directories when finding file -:PROPERTIES: -:CUSTOM_ID: h:4b9d74ef-0376-45bb-bc15-d24a04ca7e81 -:END: - -This function will check if a directory for which a file we want to open exists; if not, it will offer to create the directories for me. - -#+begin_src emacs-lisp - - (defun swarsel/with-buffer-name-prompt-and-make-subdirs () - (let ((parent-directory (file-name-directory buffer-file-name))) - (when (and (not (file-exists-p parent-directory)) - (y-or-n-p (format "Directory `%s' does not exist! Create it? " parent-directory))) - (make-directory parent-directory t)))) - - (add-to-list 'find-file-not-found-functions #'swarsel/with-buffer-name-prompt-and-make-subdirs) - -#+end_src -*** [crux] Duplicate Lines -:PROPERTIES: -:CUSTOM_ID: h:91e2f45f-54fa-4b60-8758-b2ef9b439af7 -:END: - -When programming, I like to be able to duplicate a line. There are easier functions than the one below, but they either - -1) screw with undo/redo -2) move the cursor wildly - -The below function avoids these problems. Originally I used the function =duplicate-line= found here: [[https://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs]] - -However, this function does not work on regions. Later, I found a solution implemented by [[https://github.com/bbatsov/crux][crux]]. I do not need the whole package, so I just extracted the three functions I needed from it. - -#+begin_src emacs-lisp - - (defun crux-get-positions-of-line-or-region () - "Return positions (beg . end) of the current line or region." - (let (beg end) - (if (and mark-active (> (point) (mark))) - (exchange-point-and-mark)) - (setq beg (line-beginning-position)) - (if mark-active - (exchange-point-and-mark)) - (setq end (line-end-position)) - (cons beg end))) - - (defun crux-duplicate-current-line-or-region (arg) - "Duplicates the current line or region ARG times. - If there's no region, the current line will be duplicated. However, if - there's a region, all lines that region covers will be duplicated." - (interactive "p") - (pcase-let* ((origin (point)) - (`(,beg . ,end) (crux-get-positions-of-line-or-region)) - (region (buffer-substring-no-properties beg end))) - (dotimes (_i arg) - (goto-char end) - (newline) - (insert region) - (setq end (point))) - (goto-char (+ origin (* (length region) arg) arg)))) - - (defun crux-duplicate-and-comment-current-line-or-region (arg) - "Duplicates and comments the current line or region ARG times. - If there's no region, the current line will be duplicated. However, if - there's a region, all lines that region covers will be duplicated." - (interactive "p") - (pcase-let* ((origin (point)) - (`(,beg . ,end) (crux-get-positions-of-line-or-region)) - (region (buffer-substring-no-properties beg end))) - (comment-or-uncomment-region beg end) - (setq end (line-end-position)) - (dotimes (_ arg) - (goto-char end) - (newline) - (insert region) - (setq end (point))) - (goto-char (+ origin (* (length region) arg) arg)))) - -#+end_src -*** [prot] org-id-headings -:PROPERTIES: -:CUSTOM_ID: h:4819720a-9220-4c34-b903-ed4179f3ad1c -:END: - -These functions by protesilaos generate heading links in an org-file similar to the normal =org-store-link= approach when not using properties. This approach has a weakness however - if the heading name is changed, the link breaks. These functions generate a unique identifier for each heading which will not break and also works when exporting the file to html, for example. - -#+begin_src emacs-lisp - - (defun prot-org--id-get () - "Get the CUSTOM_ID of the current entry. - If the entry already has a CUSTOM_ID, return it as-is, else - create a new one." - (let* ((pos (point)) - (id (org-entry-get pos "CUSTOM_ID"))) - (if (and id (stringp id) (string-match-p "\\S-" id)) - id - (setq id (org-id-new "h")) - (org-entry-put pos "CUSTOM_ID" id) - id))) - - (declare-function org-map-entries "org") - - (defun prot-org-id-headlines () - "Add missing CUSTOM_ID to all headlines in current file." - (interactive) - (org-map-entries - (lambda () (prot-org--id-get)))) - - (defun prot-org-id-headline () - "Add missing CUSTOM_ID to headline at point." - (interactive) - (prot-org--id-get)) - -#+end_src -*** Inhibit Messages in Echo Area -:PROPERTIES: -:CUSTOM_ID: h:285e5c0a-875d-46a8-bb9b-0222b3d73878 -:END: - -Emacs likes to send messages to the echo area; this is generally a good thing. However, it bothers me a lot when I am currently working in minibuffer where I receive an echo area message that is actually important and it is then overwritten by e.g. the mu4e update message. This section makes it possible to find the root function calling the message function and disabling it here. - -Usage: Enable the =(advice-add 'message :around #'who-called-me?)= by running this code block, which will show a full trace of all messages being sent to the echo area: - -#+begin_src emacs-lisp :tangle no :export both :results silent - - (advice-add 'message :around #'who-called-me?) - -#+end_src - -Once the root function has been found, it can be disabled via =advice=add= as in the last block in this section. To disable the stack tracing, run =(advice-remove 'message #'who-called-me?)= or the following code block: - -#+begin_src emacs-lisp :tangle no :results silent - - (advice-remove 'message #'who-called-me?) - -#+end_src - -Lastly, individual messages can be reenabled using the =(advice-remove ' #'suppress-messages)= approach. Use this when you accidentally disabled a helpful message. - - -#+begin_src emacs-lisp - - (defun suppress-messages (old-fun &rest args) - (cl-flet ((silence (&rest args1) (ignore))) - (advice-add 'message :around #'silence) - (unwind-protect - (apply old-fun args) - (advice-remove 'message #'silence)))) - - (advice-add 'pixel-scroll-precision :around #'suppress-messages) - (advice-add 'mu4e--server-filter :around #'suppress-messages) - (advice-add 'org-unlogged-message :around #'suppress-messages) - (advice-add 'magit-auto-revert-mode--init-kludge :around #'suppress-messages) - (advice-add 'push-mark :around #'suppress-messages) - - ;; to reenable - ;; (advice-remove 'timer-event-handler #'suppress-messages) - - (defun who-called-me? (old-fun format &rest args) - (let ((trace nil) (n 1) (frame nil)) - (while (setf frame (backtrace-frame n)) - (setf n (1+ n) - trace (cons (cadr frame) trace)) ) - (apply old-fun (concat "<<%S>>\n" format) (cons trace args)))) - - ;; enable to get message backtrace, the first function shown in backtrace calls the other functions - ;; (advice-add 'message :around #'who-called-me?) - - ;; disable to stop receiving backtrace - (advice-remove 'message #'who-called-me?) - - -#+end_src -*** Move up one directory for find-file -:PROPERTIES: -:CUSTOM_ID: h:de249f2a-6a2b-4114-8046-09d1014a7391 -:END: - -I find it very annoying that the standard behavior for M-DEL only deletes one word when using find-file. This function makes it so that we always go up by one directory level instead. - -This function was found here: [[https://www.reddit.com/r/emacs/comments/re31i6/how_to_go_up_one_directory_when_using_findfile_cx/]] - -#+begin_src emacs-lisp - - (defun up-directory (path) - "Move up a directory in PATH without affecting the kill buffer." - (interactive "p") - (if (string-match-p "/." (minibuffer-contents)) - (let ((end (point))) - (re-search-backward "/.") - (forward-char) - (delete-region (point) end)))) - - (define-key minibuffer-local-filename-completion-map - [C-backspace] #'up-directory) - -#+end_src -*** org-mode: General setup -:PROPERTIES: -:CUSTOM_ID: h:06b77d28-3fd5-4554-8c7d-32c1b0ec8da5 -:END: - -Sets up the basic settings that I want to have active in org-mode buffers. - -Used here: [[#h:877c9401-a354-4e44-a235-db1a90d19e00][General org-mode]] - -#+begin_src emacs-lisp - - (defun swarsel/org-mode-setup () - (org-indent-mode) - (variable-pitch-mode 1) - ;;(auto-fill-mode 0) - (setq display-line-numbers-type 'relative - display-line-numbers-current-absolute 1 - display-line-numbers-width-start nil - display-line-numbers-width 6 - display-line-numbers-grow-only 1) - (add-hook 'org-tab-first-hook 'org-end-of-line) - (visual-line-mode 1)) - -#+end_src -*** org-mode: Visual-fill column -:PROPERTIES: -:CUSTOM_ID: h:fa710375-2efe-49b4-af6a-a875aca6e4a2 -:END: - -This function sets the width of buffers in org-mode. - -Used in: [[#h:bbcfa895-4d46-4b1d-b84e-f634e982c46e][Centered org-mode Buffers]] - -#+begin_src emacs-lisp - - (defun swarsel/org-mode-visual-fill () - (setq visual-fill-column-width 150 - visual-fill-column-center-text t) - (visual-fill-column-mode 1)) - -#+end_src - -*** org-mode: Auto-tangle and export Configuration Files -:PROPERTIES: -:CUSTOM_ID: h:59d4306e-9b73-4b2c-b039-6a6518c357fc -:END: - -This section automatically tangles all configuration blocks in this file to the defined Emacs org-file. It also exports the configuration file as html. - - - -#+begin_src emacs-lisp - - (defun swarsel/org-babel-tangle-config () - (when (string-equal (buffer-file-name) - swarsel-emacs-org-filepath) - ;; Dynamic scoping to the rescue - (let ((org-confirm-babel-evaluate nil)) - (org-html-export-to-html) - (org-babel-tangle))) - (when (string-equal (buffer-file-name) - swarsel-nix-org-filepath) - ;; Dynamic scoping to the rescue - (let ((org-confirm-babel-evaluate nil)) - (org-babel-tangle)))) - - (setq org-html-htmlize-output-type nil) - - (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config))) - - - -#+end_src - -*** org-mode: Fold current heading -:PROPERTIES: -:CUSTOM_ID: h:dfa66e78-5748-45e3-a975-db3da104bb3a -:END: - -Normally emacs cycles between three states: - -1) fully folded -2) One heading expanded -3) All headings expanded - -However, I want to be able to fold a single heading consistently. - -#+begin_src emacs-lisp - -(defun org-fold-outer () - (interactive) - (org-beginning-of-line) - (if (string-match "^*+" (thing-at-point 'line t)) - (outline-up-heading 1)) - (outline-hide-subtree) - ) - -#+end_src - -*** corfu: Do not interrupt navigation -:PROPERTIES: -:CUSTOM_ID: h:a1802f9b-bb71-4fd5-86fa-945da18e8b81 -:END: - -These three functions allow me to keep using the normal navigation keys even when a corfu completion pops up. - -These functions are used here: [[#h:5653d693-ecca-4c95-9633-66b9e3241070][Corfu]] - -#+begin_src emacs-lisp - - (defun swarsel/corfu-normal-return (&optional arg) - (interactive) - (corfu-quit) - (newline) - ) - - (defun swarsel/corfu-quit-and-up (&optional arg) - (interactive) - (corfu-quit) - (evil-previous-visual-line)) - - (defun swarsel/corfu-quit-and-down (&optional arg) - (interactive) - (corfu-quit) - (evil-next-visual-line)) - -#+end_src - -*** python shell reloading - -The standard Emacs behaviour for the Python process shell is a bit annoying. This is my attempt at making it show automatically on opening a python buffer and making it refresh on its own as well. This does not nicely work yet. - -#+begin_src emacs-lisp - - ;; run the python inferior shell immediately upon entering a python buffer - ;; (add-hook 'python-mode-hook 'swarsel/run-python) - - ;; (defun swarsel/run-python () - ;; (save-selected-window - ;; (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) - - ;; reload python shell automatically - (defun my-python-shell-run () - (interactive) - (when (get-buffer-process "*Python*") - (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil) - (kill-process (get-buffer-process "*Python*")) - ;; Uncomment If you want to clean the buffer too. - ;;(kill-buffer "*Python*") - ;; Not so fast! - (sleep-for 0.5)) - (run-python (python-shell-parse-command) nil nil) - (python-shell-send-buffer) - ;; Pop new window only if shell isnt visible - ;; in any frame. - (unless (get-buffer-window "*Python*" t) - (python-shell-switch-to-shell))) - - (defun my-python-shell-run-region () - (interactive) - (python-shell-send-region (region-beginning) (region-end)) - (python-shell-switch-to-shell)) - -#+end_src - - -** Custom Keybindings -:PROPERTIES: -:CUSTOM_ID: h:2b827c27-0de7-45ed-9d9e-6c511e2c6bb5 -:END: - -This defines a set of keybinds that I want to have available globally. I have one set of keys that is globally available through the =C-SPC= prefix. This set is used mostly for functions that I have trouble remembering the original keybind for, or that I just want to have gathered in a common space. - -I also define some keybinds to some combinations directly. Those are used mostly for custom functions that I call often enough to warrant this. - -#+begin_src emacs-lisp - - ;; Make ESC quit prompts - (global-set-key (kbd "") 'keyboard-escape-quit) - - ;; Set up general keybindings - (use-package general - :config - (general-create-definer swarsel/leader-keys - :keymaps '(normal insert visual emacs) - :prefix "SPC" - :global-prefix "C-SPC") - - (swarsel/leader-keys - "e" '(:ignore e :which-key "evil") - "eo" '(evil-jump-backward :which-key "cursor jump backwards") - "eO" '(evil-jump-forward :which-key "cursor jump forwards") - "t" '(:ignore t :which-key "toggles") - "ts" '(hydra-text-scale/body :which-key "scale text") - "te" '(swarsel/toggle-evil-state :which-key "emacs/evil") - "tl" '(display-line-numbers-mode :which-key "line numbers") - "tp" '(evil-cleverparens-mode :wk "cleverparens") - "to" '(olivetti-mode :wk "olivetti") - "td" '(darkroom-tentative-mode :wk "darkroom") - "tw" '((lambda () (interactive) (toggle-truncate-lines)) :which-key "line wrapping") - "m" '(:ignore m :which-key "modes/programs") - "mm" '((lambda () (interactive) (mu4e)) :which-key "mu4e") - "mg" '((lambda () (interactive) (magit-list-repositories)) :which-key "magit-list-repos") - "mc" '((lambda () (interactive) (swarsel/open-calendar)) :which-key "calendar") - "mp" '(popper-toggle :which-key "popper") - "md" '(dirvish :which-key "dirvish") - "o" '(:ignore o :which-key "org") - "op" '((lambda () (interactive) (org-present)) :which-key "org-present") - "ob" '((lambda () (interactive) (org-babel-mark-block)) :which-key "Mark whole src-block") - "ol" '((lambda () (interactive) (org-insert-link)) :which-key "insert link") - "os" '((lambda () (interactive) (org-store-link)) :which-key "store link") - "od" '((lambda () (interactive) (org-babel-demarcate-block)) :which-key "demarcate (split) src-block") - ;; "c" '(:ignore c :which-key "capture") - ;; "cj" '((lambda () (interactive) (org-capture nil "jj")) :which-key "journal") - ;; "cs" '(markdown-download-screenshot :which-key "screenshot") - "l" '(:ignore l :which-key "links") - "le" '((lambda () (interactive) (find-file swarsel-emacs-org-filepath)) :which-key "Emacs.org") - "ls" '((lambda () (interactive) (find-file "/smb:Swarsel@192.168.1.3:")) :which-key "Server") - "lo" '(dired swarsel-obsidian-vault-directory :which-key "obsidian") - ;; "la" '((lambda () (interactive) (find-file swarsel-org-anki-filepath)) :which-key "anki") - "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org") - "lp" '((lambda () (interactive) (projectile-switch-project)) :which-key "switch project") - "lg" '((lambda () (interactive) (magit-list-repositories)) :which-key "list git repos") - ;; "a" '(:ignore a :which-key "anki") - ;; "ap" '(anki-editor-push-tree :which-key "push new cards") - ;; "an" '((lambda () (interactive) (org-capture nil "a")) :which-key "new card") - ;; "as" '(swarsel-anki-set-deck-and-notetype :which-key "change deck and notetype") - "h" '(:ignore h :which-key "help") - "hy" '(yas-describe-tables :which-key "yas tables") - "hb" '(embark-bindings :which-key "current key bindings") - "h" '(:ignore t :which-key "describe") - "he" 'view-echo-area-messages - "hf" 'describe-function - "hF" 'describe-face - "hl" '(view-lossage :which-key "show command keypresses") - "hL" 'find-library - "hm" 'describe-mode - "ho" 'describe-symbol - "hk" 'describe-key - "hK" 'describe-keymap - "hp" 'describe-package - "hv" 'describe-variable - "hd" 'devdocs-lookup - "w" '(:ignore t :which-key "window") - "wl" 'windmove-right - "wh" 'windmove-left - "wk" 'windmove-up - "wj" 'windmove-down - "wr" 'winner-redo - "wd" 'delete-window - "w=" 'balance-windows-area - "wD" 'kill-buffer-and-window - "wu" 'winner-undo - "wr" 'winner-redo - "w/" 'evil-window-vsplit - "w-" 'evil-window-split - "wm" '(delete-other-windows :wk "maximize") - )) - - ;; General often used hotkeys - (general-define-key - "C-M-a" (lambda () (interactive) (org-capture nil "a")) ; make new anki card - ;; "C-M-d" 'swarsel-obsidian-daily ; open daily obsidian file and create if not exist - ;; "C-M-S" 'swarsel-anki-set-deck-and-notetype ; switch deck and notetype for new anki cards - ;; "C-M-s" 'markdown-download-screenshot ; wrapper for org-download-screenshot - "C-c d" 'crux-duplicate-current-line-or-region - "C-c D" 'crux-duplicate-and-comment-current-line-or-region - "" 'swarsel/last-buffer - "M-\\" 'indent-region - "C-" 'my-python-shell-run - ) - -#+end_src -** Directory setup -:PROPERTIES: -:CUSTOM_ID: h:07951589-54ba-4e3e-bd7b-4106cd22ff6a -:END: - -In this section I setup some aliases that I use for various directories on my system. Some of these are actually used for magit repository finding etc., but many of them serve no real use and I need to clean this up someday. - -#+begin_src emacs-lisp - - ;; set Nextcloud directory for journals etc. - (setq swarsel-sync-directory "~/Nextcloud" - swarsel-emacs-directory "~/.emacs.d" - swarsel-dotfiles-directory "~/.dotfiles" - swarsel-projects-directory "~/Documents/GitHub") - - (setq swarsel-emacs-org-filepath (expand-file-name "Emacs.org" swarsel-dotfiles-directory) - swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory)) - - - ;; set Emacs main configuration .org names - (setq swarsel-emacs-org-file "Emacs.org" - swarsel-anki-org-file "Anki.org" - swarsel-tasks-org-file "Tasks.org" - swarsel-archive-org-file "Archive.org" - swarsel-org-folder-name "Org" - swarsel-obsidian-daily-folder-name "⭐ Personal/Journal" - swarsel-obsidian-folder-name "Obsidian" - swarsel-obsidian-vault-name "Main") - - - ;; set directory paths - (setq swarsel-org-directory (expand-file-name swarsel-org-folder-name swarsel-sync-directory)) ; path to org folder - (setq swarsel-obsidian-directory (expand-file-name swarsel-obsidian-folder-name swarsel-sync-directory)) ; path to obsidian - (setq swarsel-obsidian-vault-directory (expand-file-name swarsel-obsidian-vault-name swarsel-obsidian-directory)) ; path to obsidian vault - (setq swarsel-obsidian-daily-directory (expand-file-name swarsel-obsidian-daily-folder-name swarsel-obsidian-vault-directory)) ; path to obsidian daily folder - - ;; filepaths to certain documents - (setq swarsel-org-anki-filepath (expand-file-name swarsel-anki-org-file swarsel-org-directory) ; path to anki export file - swarsel-org-tasks-filepath (expand-file-name swarsel-tasks-org-file swarsel-org-directory) - swarsel-org-archive-filepath (expand-file-name swarsel-archive-org-file swarsel-org-directory)) - - ;; set paths to authentication files (forge) - ;; (setq auth-source-pass-filename "~/.local/share/password-store" - (setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") - auth-source-cache-expiry nil) ; default is 2h - - - -#+end_src - -** Unclutter .emacs.d -:PROPERTIES: -:CUSTOM_ID: h:0cf30b76-91d9-41da-a10b-74199bc36d40 -:END: - -In this section I move the =custom.el= out of it's standard location in =.emacs.d=. Firstly, I dislike using this file at all since I would rather have fully stateful configuration as commanded by this file. Secondly, this file is too easily permanently changed. Recently I figured out the last bits that I needed to remove from custom.el to no longer be reliant on it, so I now just write it to a temporary file (through =make-temp=file=) which will be cleaned on shutdown. However, I like to retain the custom framework because it is nice for testing out theme customizations, hence why I still load the file. - -This section also sets the emacs directory to the =~/.cache/= directory which is useful for files that I do not want to have lying around in my =.emacs.d=. - -#+begin_src emacs-lisp - - ;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d - (setq user-emacs-directory (expand-file-name "~/.cache/emacs/") - url-history-file (expand-file-name "url/history" user-emacs-directory)) - - ;; Use no-littering to automatically set common paths to the new user-emacs-directory - (use-package no-littering) - (setq custom-file (make-temp-file "emacs-custom-")) - (load custom-file t) - -#+end_src - -** Move backup files to another location -:PROPERTIES: -:CUSTOM_ID: h:329f529a-ef9f-4787-b311-1c485e05b754 -:END: - -Many people dislike the Emacs backup files; I do enjoy them, but have to admit that they clutter the filesystem a little too much. Also, I rarely need to access these over different sessions. Hence I move them to =/tmp= - if Emacs unexpectedly crashes, the files can be recovered, but the backup files will not gather everywhere and will be deleted upon shutdown. - -#+begin_src emacs-lisp - - (let ((backup-dir "~/tmp/emacs/backups") - (auto-saves-dir "~/tmp/emacs/auto-saves/")) - (dolist (dir (list backup-dir auto-saves-dir)) - (when (not (file-directory-p dir)) - (make-directory dir t))) - (setq backup-directory-alist `(("." . ,backup-dir)) - auto-save-file-name-transforms `((".*" ,auto-saves-dir t)) - auto-save-list-file-prefix (concat auto-saves-dir ".saves-") - tramp-backup-directory-alist `((".*" . ,backup-dir)) - tramp-auto-save-directory auto-saves-dir)) - - (setq backup-by-copying t ; Don't delink hardlinks - delete-old-versions t ; Clean up the backups - version-control t ; Use version numbers on backups, - kept-new-versions 5 ; keep some new versions - kept-old-versions 2) ; and some old ones, too - -#+end_src -* General init.el setup + UI -:PROPERTIES: -:CUSTOM_ID: h:786b447d-03ad-4c1d-b114-c37caa2d591c -:END: - -In this general section I have settings that I either consider to be integral to my experience when using emacs or have no other section that I feel they belong to. - -** General setup -:PROPERTIES: -:CUSTOM_ID: h:76a5bd78-a20d-4068-bea8-a38fdb26428e -:END: - -Here I set up some things that are too minor to put under other categories. -- Firstly we disable to having to type `yes` and `no` and switch it to `y` and `n`. -- We also enable the marking of trailing whitespaces. -- Also, make emacs highlight the current line globally -- Emacs defaults to pausing all display redrawing on any input. This may have been useful previously, but is not necessary nowadays. -- I also disable the suspend-frame function, as I never use it and it is quite confusing when accidentally hitting the keys for it. - -#+begin_src emacs-lisp - - ;; use UTF-8 everywhere - (set-language-environment "UTF-8") - - ;; set default font size - (defvar swarsel/default-font-size 130) - (setq swarsel-standard-font "FiraCode Nerd Font Mono" - swarsel-alt-font "FiraCode Nerd Font Mono") - - ;; (defalias 'yes-or-no-p 'y-or-n-p) - ;;(setq-default show-trailing-whitespace t) - (add-hook 'before-save-hook 'delete-trailing-whitespace) - (global-hl-line-mode 1) - ;; (setq redisplay-dont-pause t) ;; obsolete - (setq blink-cursor-mode nil) ;; blink-cursor is an unexpected source of slowdown - (global-subword-mode 1) ; Iterate through CamelCase words - (setq blink-matching-paren nil) ;; this makes the cursor jump around annoyingly - (delete-selection-mode 1) - (setq vc-follow-symlinks t) - (setq require-final-newline t) - (winner-mode 1) - (setq load-prefer-newer t) - - (setq undo-limit 80000000 - evil-want-fine-undo t - auto-save-default t - password-cache-expiry nil - ) - (setq browse-url-browser-function 'browse-url-firefox) - ;; disable a keybind that does more harm than good - (global-set-key [remap suspend-frame] - (lambda () - (interactive) - (message "This keybinding is disabled (was 'suspend-frame')"))) - - (setq visible-bell nil) - (setq initial-major-mode 'fundamental-mode - initial-scratch-message nil) - - (add-hook 'prog-mode-hook 'display-line-numbers-mode) - (add-hook 'text-mode-hook 'display-line-numbers-mode) - -#+end_src - -** Mark all themes as safe -:PROPERTIES: -:CUSTOM_ID: h:0debe8fd-b319-4ab7-a92c-784fa7896b75 -:END: - -Normally when switching themes in emacs, the user will be warned that themes can run malicious code. I only run one theme really and deem it safe. It is however annoying to be asked this on every new system and it also creates lines in custom.el to answer that query, so here I declare all themes as safe. - -#+begin_src emacs-lisp - - (setq custom-safe-themes t) - -#+end_src -** Show less compilation warnings -:PROPERTIES: -:CUSTOM_ID: h:b587e869-9911-443b-bc6d-8fb3ce31908d -:END: - -When Emacs compiles stuff, it often shows a bunch of warnings that I do not need to deal with. Here we silence those. Some will be disabled completely, and some only when we have native compilation available (which should be most of the time, however). - -#+begin_src emacs-lisp - - (setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) - ;; Make native compilation silent and prune its cache. - (when (native-comp-available-p) - (setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation - (setq native-compile-prune-cache t)) ; Emacs 29 - -#+end_src - -** Indentation -:PROPERTIES: -:CUSTOM_ID: h:6527b3ce-b76d-431a-9960-a57da7c53e1b -:END: - -Here I define several options related to indentation; I first make it so that only whitespace will be used instead of tab characters for indentation, and I also set a small standard indent. - -We set =tab-always-indent= to ='complete= in order to indent first and then do completion if there are any. Also we make it so that python will not complain about missing indentation info. - -Lastly, I load the =highlight-indent-guides= package. This adds a neat visual indicator of the indentation level, which is useful for languages like python. - -#+begin_src emacs-lisp - - (setq-default indent-tabs-mode nil - tab-width 2) - - (setq tab-always-indent 'complete) - (setq python-indent-guess-indent-offset-verbose nil) - - (use-package highlight-indent-guides - :hook (prog-mode . highlight-indent-guides-mode) - :init - (setq highlight-indent-guides-method 'column) - (setq highlight-indent-guides-responsive 'top) - ) - - (with-eval-after-load 'highlight-indent-guides - (set-face-attribute 'highlight-indent-guides-even-face nil :background "gray10") - (set-face-attribute 'highlight-indent-guides-odd-face nil :background "gray20") - (set-face-attribute 'highlight-indent-guides-stack-even-face nil :background "gray40") - (set-face-attribute 'highlight-indent-guides-stack-odd-face nil :background "gray50")) -#+end_src - -** Scrolling -:PROPERTIES: -:CUSTOM_ID: h:3dc9fb1d-cd16-4bd0-a9ac-55a944415a90 -:END: - -By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying. This sets up more granular scrolling that allows scrolling with a mouse wheel or the two-finger touchscreen gesture. This now also works in buffers with a very small frame. - -#+begin_src emacs-lisp - - (setq mouse-wheel-scroll-amount - '(1 - ((shift) . 5) - ((meta) . 0.5) - ((control) . text-scale)) - mouse-drag-copy-region nil - make-pointer-invisible t - mouse-wheel-progressive-speed t - mouse-wheel-follow-mouse t) - - (setq-default scroll-preserve-screen-position t - scroll-conservatively 1 - scroll-margin 0 - next-screen-context-lines 0) - - (pixel-scroll-precision-mode 1) - -#+end_src - -** Evil -:PROPERTIES: -:CUSTOM_ID: h:5bf9f014-ee96-42da-b285-7b34f04e6bb1 -:END: - -*** General evil -:PROPERTIES: -:CUSTOM_ID: h:218376e8-086b-46bf-91b3-78295d5d440f -:END: - -This setups up evil, which brings vim-like keybindings to emacs. In the same location, I also unbind the =C-z= key (I am very unhappy with this implementation, but it is the only thing that works consistently so far) to make it available for [[#h:c3cc1c12-3ab8-42b7-be07-63f54eac397f][cape]] later. - -Also, I setup initial modes for several major-modes depending on what I deem fit. - -#+begin_src emacs-lisp - - ;; Emulate vim in emacs - (use-package evil - :init - (setq evil-want-integration t) ; loads evil - (setq evil-want-keybinding nil) ; loads "helpful bindings" for other modes - (setq evil-want-C-u-scroll t) ; scrolling using C-u - (setq evil-want-C-i-jump nil) ; jumping with C-i - (setq evil-want-Y-yank-to-eol t) ; give Y some utility - (setq evil-shift-width 2) ; uniform indent - (setq evil-respect-visual-line-mode t) ; i am torn on this one - (setq evil-split-window-below t) - (setq evil-vsplit-window-right t) - :config - (evil-mode 1) - (define-key evil-normal-state-map (kbd "C-z") nil) - (define-key evil-insert-state-map (kbd "C-z") nil) - (define-key evil-visual-state-map (kbd "C-z") nil) - (define-key evil-motion-state-map (kbd "C-z") nil) - (define-key evil-operator-state-map (kbd "C-z") nil) - (define-key evil-replace-state-map (kbd "C-z") nil) - (define-key global-map (kbd "C-z") nil) - (evil-set-undo-system 'undo-tree) - - ;; Don't use evil-mode in these contexts, or use it in a specific mode - (evil-set-initial-state 'messages-buffer-mode 'emacs) - (evil-set-initial-state 'dashboard-mode 'emacs) - (evil-set-initial-state 'dired-mode 'emacs) - (evil-set-initial-state 'cfw:details-mode 'emacs) - (evil-set-initial-state 'Custom-mode 'emacs) ; god knows why this mode is in uppercase - (evil-set-initial-state 'mu4e-headers-mode 'normal) - (evil-set-initial-state 'python-inferior-mode 'normal) - (add-hook 'org-capture-mode-hook 'evil-insert-state) - (add-to-list 'evil-buffer-regexps '("COMMIT_EDITMSG" . insert))) - -#+end_src -*** evil-collection -:PROPERTIES: -:CUSTOM_ID: h:bde208f3-01ef-4dc6-9981-65f3d2a8189b -:END: - -This gives support for many different modes, and works beautifully out of the box. - -#+begin_src emacs-lisp - - (use-package evil-collection - :after evil - :config - (evil-collection-init) - (setq forge-add-default-bindings nil)) -#+end_src -*** evil-snipe -:PROPERTIES: -:CUSTOM_ID: h:d80e3f7d-0185-4a15-832b-d756e576265c -:END: - -This package changes the char-search commands like =f= by showing the results in a more visual manner. It also gives a 2-character search using =s= and =S=. - -#+begin_src emacs-lisp - ;; enables 2-char inline search - (use-package evil-snipe - :after evil - :demand - :config - (evil-snipe-mode +1) - ;; replace 1-char searches (f&t) with this better UI - (evil-snipe-override-mode +1)) -#+end_src - -*** evil-cleverparens -:PROPERTIES: -:CUSTOM_ID: h:b06a378d-5248-4451-8eee-e65a3a768b1d -:END: - -This helps keeping parentheses balanced which is useful when writing in languages like Elisp. I do not activate this by default, as most languages do not profit from this enough in my eyes. - -#+begin_src emacs-lisp - ;; for parentheses-heavy languades modify evil commands to keep balance of parantheses - (use-package evil-cleverparens) - -#+end_src - -*** evil-surround -:PROPERTIES: -:CUSTOM_ID: h:aac82e5e-d882-4870-b644-ebdd0a2daae3 -:END: - -This minor-mode adds functionality for doing better surround-commands; for example =ci[= will let you change the word within square brackets. - -#+begin_src emacs-lisp - - ;; enables surrounding text with S - (use-package evil-surround - :config - (global-evil-surround-mode 1)) - -#+end_src - -** ispell -:PROPERTIES: -:CUSTOM_ID: h:e888d7a7-1755-4109-af11-5358b8cf140e -:END: - -This should setup a wordlist that can be used as a dictionary. However, for some reason this does not work, and I will need to further investigate this issue. - -#+begin_src emacs-lisp - - ;; set the NixOS wordlist by hand - (setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST") - -#+end_src - -** Font Configuration -:PROPERTIES: -:CUSTOM_ID: h:60f87342-0491-4c56-8057-6f075cf35753 -:END: - -Here I define my fonts to be used. Honestly I do not understand the face-attributes and pitches of emacs all too well. It seems this configuration works fine, but I might have to revisit this at some point in the future. - -#+begin_src emacs-lisp - - (dolist (face '(default fixed-pitch)) - (set-face-attribute face nil - :font "FiraCode Nerd Font Mono")) - (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono")) - - (set-face-attribute 'default nil :height 100) - (set-face-attribute 'fixed-pitch nil :height 1.0) - - (set-face-attribute 'variable-pitch nil - :family "IBM Plex Sans" - :weight 'regular - :height 1.06) - - ;; these settings used to be in custom.el - -#+end_src - -** Theme -:PROPERTIES: -:CUSTOM_ID: h:72a9704b-83d2-4b74-a1f6-d333203f62db -:END: - -I have grown to love the =doom-citylights= theme and have modeled my whole system after it. Also solaire-mode is a nice mode that inverts the alt-faces with the normal faces for specific 'minor' buffers (like Help-buffers). - -#+begin_src emacs-lisp - - (use-package solaire-mode - :custom - (solaire-global-mode +1)) - - (use-package doom-themes - :hook - (server-after-make-frame . (lambda () (load-theme - 'doom-city-lights t))) - :config - (load-theme 'doom-city-lights t) - (doom-themes-treemacs-config) - (doom-themes-org-config)) - -#+end_src - -** Icons -:PROPERTIES: -:CUSTOM_ID: h:eb0ea526-a83a-4664-b3a1-2b40d3a31493 -:END: - -This section loads the base icons used in my configuration. I am using =nerd-icons= over =all-the-icons= since the former seems to have more integrations with different packages than the latter. - -Used in: -- [[#h:b190d512-bfb5-42ec-adec-8d86bab726ce][Vertico and friends]] -- [[#h:5653d693-ecca-4c95-9633-66b9e3241070][IN USE Corfu]] - -#+begin_src emacs-lisp - - (use-package nerd-icons) - -#+end_src - -** Variable Pitch Mode -:PROPERTIES: -:CUSTOM_ID: h:455ed7ac-ee7f-4f94-b857-f2c58b2282d0 -:END: - -This minor mode allows mixing fixed and variable pitch fonts within the same buffer. - -#+begin_src emacs-lisp - -(use-package mixed-pitch - :custom - (mixed-pitch-set-height nil) - (mixed-pitch-variable-pitch-cursor nil) - :hook - (text-mode . mixed-pitch-mode)) - - -#+end_src - -** Modeline -:PROPERTIES: -:CUSTOM_ID: h:ed585848-875a-4673-910c-d2e1901dd95b -:END: - -Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline. Most informations I disable for it, except for the cursor information (row + column) as well as a widget for =mu4e= and git information. - -#+begin_src emacs-lisp - - (use-package doom-modeline - :init - (doom-modeline-mode) - (column-number-mode) - :custom - ((doom-modeline-height 22) - (doom-modeline-indent-info nil) - (doom-modeline-buffer-encoding nil))) - - -#+end_src - -** Helper Modes -:PROPERTIES: -:CUSTOM_ID: h:39ae01e9-8053-4f76-aa77-8cbbbcff9652 -:END: -*** Vertico, Orderless, Marginalia, Consult, Embark -:PROPERTIES: -:CUSTOM_ID: h:b190d512-bfb5-42ec-adec-8d86bab726ce -:END: - -This set of packages uses the default emacs completion framework and works together to provide a very nice user experience: - -- Vertico simply provides a vertically stacking completion -- Marginalia adds more information to completion results -- Orderless allows for fuzzy matching -- Consult provides better implementations for several user functions, e.g. =consult-line= or =consult-outline= -- Embark allows acting on the results in the minibuffer while the completion is still ongoing - this is extremely useful since it allows to, for example, read the documentation for several functions without closing the help search. It can also collect the results of a grep operation into a seperate buffer that edits the result in their original location. - -Nerd icons is originally enabled here: [[#h:eb0ea526-a83a-4664-b3a1-2b40d3a31493][Icons]] - -**** vertico -:PROPERTIES: -:CUSTOM_ID: h:d7c7f597-f870-4e01-8f7e-27dd31dd245d -:END: - -#+begin_src emacs-lisp - - (setq read-buffer-completion-ignore-case t - read-file-name-completion-ignore-case t - completion-ignore-case t) - - (use-package vertico - :custom - (vertico-scroll-margin 0) - (vertico-count 10) - (vertico-resize t) - (vertico-cycle t) - :init - (vertico-mode) - (vertico-mouse-mode)) -#+end_src - -**** vertico-directory -:PROPERTIES: -:CUSTOM_ID: h:10d4f2bd-8c72-430b-a9ed-9b5e279ec0b4 -:END: - -This package allows for =Ido=-like directory navigation. - -#+begin_src emacs-lisp - (use-package vertico-directory - :ensure nil - :after vertico - :bind (:map vertico-map - ("RET" . vertico-directory-enter) - ("C-DEL" . vertico-directory-delete-word) - ("DEL" . vertico-directory-delete-char)) - ;; Tidy shadowed file names - :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) - -#+end_src - -**** orderless -:PROPERTIES: -:CUSTOM_ID: h:211fc0bd-0d64-4577-97d8-6abc94435f04 -:END: - -The completion styles that I chose here can possibly still be improved. I need to spend more time on this. - -#+begin_src emacs-lisp - - (use-package orderless - :custom - (completion-styles '(orderless flex basic)) - (completion-category-overrides '((file (styles . (partial-completion))) - (eglot (styles orderless))))) - -#+end_src - -**** consult -:PROPERTIES: -:CUSTOM_ID: h:49ab82bf-812d-4fbe-a5b6-d3ad703fe32c -:END: - -The big winner here are the convenient keybinds being setup here for general use. Also, I setup vim-navigation for minibuffer completions. =consult-buffer= is set twice because I am still used to that weird =C-M-j= command that I chose for =ivy-switch-buffer= when I first started using Emacs. I want to move to the other command but for now it is not feasible to delete the other one. - -#+begin_src emacs-lisp - - (use-package consult - :config - (setq consult-fontify-max-size 1024) - :bind - (("C-x b" . consult-buffer) - ("C-c " . consult-global-mark) - ("C-c C-a" . consult-org-agenda) - ("C-x O" . consult-org-heading) - ("C-M-j" . consult-buffer) - ("C-s" . consult-line) - ("M-g M-g" . consult-goto-line) - ("M-g i" . consult-imenu) - ("M-s M-s" . consult-line-multi) - :map minibuffer-local-map - ("C-j" . next-line) - ("C-k" . previous-line))) - -#+end_src -**** embark -:PROPERTIES: -:CUSTOM_ID: h:1c564ee5-ccd7-48be-b69a-d963400c4704 -:END: - -I have stripped down the embark keybinds heavily. It is very useful to me even in it's current state, but it quickly becomes overwhelming. =embark-dwim= acts on a candidate without closing the minibuffer, which is very useful. =embark-act= lets the user choose from all actions, but has an overwhelming interface. - -#+begin_src emacs-lisp - - (use-package embark - :bind - (("C-." . embark-act) - ("M-." . embark-dwim) - ("C-h B" . embark-bindings) - ("C-c c" . embark-collect)) - :custom - (prefix-help-command #'embark-prefix-help-command) - (embark-quit-after-action '((t . nil))) - :config - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) - -#+end_src -**** embark-consult -:PROPERTIES: -:CUSTOM_ID: h:6287551c-a6f7-4870-b3f3-210d6f038b6f -:END: - -Provides previews for embark. - -#+begin_src emacs-lisp - - (use-package embark-consult - :after (embark consult) - :demand t ; only necessary if you have the hook below - ;; if you want to have consult previews as you move around an - ;; auto-updating embark collect buffer - :hook - (embark-collect-mode . consult-preview-at-point-mode)) -#+end_src -**** marginalia -:PROPERTIES: -:CUSTOM_ID: h:f32040a4-882f-4e6b-97f1-a0105c44c034 -:END: - -I set the annotation-mode of marginalia to =heavy=. This gives even more information on the stuff that you are looking at. One thing I am missing from ivy is the highlighting on =mode=-commands based on the current state of the mode. Also, I do not understand all the shorthands used by marginalia yet. - -#+begin_src emacs-lisp - (use-package marginalia - :after vertico - :init - (marginalia-mode) - (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) - -#+end_src -**** nerd-icons-completion -:PROPERTIES: -:CUSTOM_ID: h:d70ec2fb-da43-4523-9ee4-774ececdb80e -:END: - -As stated above, this simply provides nerd-icons to the completion framework. - -#+begin_src emacs-lisp - - (use-package nerd-icons-completion - :after (marginalia nerd-icons) - :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) - :init - (nerd-icons-completion-mode)) - - -#+end_src - -*** Helpful + which-key: Better help defaults -:PROPERTIES: -:CUSTOM_ID: h:cbf6bd48-2503-489a-89da-e3359564e989 -:END: - -This pair of packages provides information on keybinds in addition to function names, which makes it easier to remember keybinds (=which-key=). The =helpful= package provides a better =Help= framework for Emacs. For some reason, the Help windows are always being focused by the cursor even though I have set =help-window-select= to nil. I do not understand why. - -#+begin_src emacs-lisp - - (use-package which-key - :init (which-key-mode) - :diminish which-key-mode - :config - (setq which-key-idle-delay 0.3)) - - (use-package helpful - :bind - (("C-h f" . helpful-callable) - ("C-h v" . helpful-variable) - ("C-h k" . helpful-key) - ("C-h C-." . helpful-at-point)) - :config - (setq help-window-select nil)) -#+end_src - -** Ligatures -:PROPERTIES: -:CUSTOM_ID: h:bbbd9cc8-3a84-4810-a3d5-b8536a5fbda1 -:END: - -Personally, I think ligatures are fancy. With this mode, they stay 'cursorable'. However, I do not need them in all modes, so I only use them in programming modes. - -#+begin_src emacs-lisp - - (use-package ligature - :init - (global-ligature-mode t) - :config - (ligature-set-ligatures 'prog-mode - '("|||>" "<|||" "<==>" "" "---" "-<<" - "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" - "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "" "###" "#_(" "..<" - "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~=" - "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|" - "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" - ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:" - "<$" "<=" "<>" "<-" "<<" "<+" "" "++" "?:" "?=" - "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\" - "://" ";;"))) - -#+end_src - -** Popup (popper) + Shackle Buffers -:PROPERTIES: -:CUSTOM_ID: h:e9d40e63-0e1f-47df-98f7-5427992588a4 -:END: - -The popper package allows to declare different buffers as 'popup-type', which sort of acts like a scratchpad. It can be toggled at any time using =popper-toggle= and the resulting frame can be freely customized (with =shackle=) to a certain size. It is also possible to prevent a buffer from appearing - I do this for example to the =*Warnings*= buffer, since usually I am not interested in it's output. - -=popper-echo-mode= shows all buffers that are currently stored as a popup in the echo area when a popup is opened - this is useful since you can cycle between all popup buffers. - -#+begin_src emacs-lisp - - (use-package popper - :bind (("M-[" . popper-toggle)) - :init - (setq popper-reference-buffers - '("\\*Messages\\*" - ("\\*Warnings\\*" . hide) - "Output\\*$" - "\\*Async Shell Command\\*" - "\\*Async-native-compile-log\\*" - help-mode - helpful-mode - "*Occur*" - "*scratch*" - "*julia*" - "*Python*" - ;; ("*tex-shell*" . hide) - (compilation-mode . hide))) - (popper-mode +1) - (popper-echo-mode +1)) - - (use-package shackle - :config - (setq shackle-rules '(("*Messages*" :select t :popup t :align right :size 0.3) - ("*Warnings*" :ignore t :popup t :align right :size 0.3) - ("*Occur*" :select t :popup t :align below :size 0.2) - ("*scratch*" :select t :popup t :align below :size 0.2) - ("*Python*" :select t :popup t :align below :size 0.2) - ("*tex-shell*" :ignore t :popup t :align below :size 0.2) - (helpful-mode :select t :popup t :align right :size 0.35) - (help-mode :select t :popup t :align right :size 0.4))) - (shackle-mode 1)) - -#+end_src -** Indicate first and last line of buffer -:PROPERTIES: -:CUSTOM_ID: h:a6d23c8c-125f-4e36-af30-ff0a1e0d5a28 -:END: - -This places little angled indicators on the fringe of a window which indicate buffer boundaries. This is not super useful, but makes use of a space that I want to keep for aesthetic reasons anyways and makes it a bit more useful in the process. - -#+begin_src emacs-lisp - -(setq-default indicate-buffer-boundaries t) - -#+end_src - -* Modules -:PROPERTIES: -:CUSTOM_ID: h:f2622fd3-7f14-47a8-8c21-33574fcbf14b -:END: - -This section houses all configuration bits that are related to a specific package that is not fundamental to my Emacs experience. - -At some point this will receive further sorting, but for now this is good enough. - -** Org Mode -:PROPERTIES: -:CUSTOM_ID: h:99544398-72af-4382-b8e1-01b2221baff4 -:END: - -org-mode is probably my most-used mode in Emcas. It acts as my organizer, config management tool and calender even. - -Note that nearly all headings within the =Org-mode= heading are coded within the =use-package= setup, so be very careful about moving stuff about here. - -*** General org-mode -:PROPERTIES: -:CUSTOM_ID: h:877c9401-a354-4e44-a235-db1a90d19e00 -:END: - -This sets up the basic org-mode. I wrote a function to handle some of the initial org-mode behaviour in [[#h:06b77d28-3fd5-4554-8c7d-32c1b0ec8da5][org-mode setup. -]] -This part of the configuration mostly makes some aesthetic changes, enables neat LaTeX and points Emacs to some files that it needs for org-mode - -#+begin_src emacs-lisp - - (use-package org - ;;:diminish (org-indent-mode) - :hook (org-mode . swarsel/org-mode-setup) - :bind - (("C-" . org-fold-outer) - ("C-c s" . org-store-link)) - :config - (setq org-ellipsis " ⤵" - org-link-descriptive t - org-hide-emphasis-markers t) - (setq org-startup-folded t) - (setq org-support-shift-select t) - - ;; (setq org-agenda-start-with-log-mode t) - ;; (setq org-log-done 'time) - ;; (setq org-log-into-drawer t) - (setq org-startup-with-inline-images t) - (setq org-image-actual-width nil) - (setq org-format-latex-options '(:foreground "White" :background default :scale 2.0 :html-foreground "Black" :html-background "Transparent" :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\["))) - -#+end_src -*** org-agenda -:PROPERTIES: -:CUSTOM_ID: h:2b3b4eb6-68a1-476d-b5d1-940a21484f1d -:END: - -Here I setup a plethora of keywords, keybinds and paths to give my org-agenda more power. - -#+begin_src emacs-lisp - - (setq org-agenda-files '("/home/swarsel/Nextcloud/Org/Tasks.org" - "/home/swarsel/Nextcloud/Org/Archive.org" - "/home/swarsel/Nextcloud/Org/Anki.org" - "/home/swarsel/Calendars/leon_cal.org")) - - (setq org-refile-targets - '((swarsel-archive-org-file :maxlevel . 1) - (swarsel-anki-org-file :maxlevel . 1) - (swarsel-tasks-org-file :maxlevel . 1))) - - (setq org-todo-keywords - '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)") - (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)"))) - - - ;; Configure custom agenda views - (setq org-agenda-custom-commands - '(("d" "Dashboard" - ((agenda "" ((org-deadline-warning-days 7))) - (todo "NEXT" - ((org-agenda-overriding-header "Next Tasks"))) - (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects"))))) - - ("n" "Next Tasks" - ((todo "NEXT" - ((org-agenda-overriding-header "Next Tasks"))))) - - ("W" "Work Tasks" tags-todo "+work-email") - - - ("w" "Workflow Status" - ((todo "WAIT" - ((org-agenda-overriding-header "Waiting on External") - (org-agenda-files org-agenda-files))) - (todo "REVIEW" - ((org-agenda-overriding-header "In Review") - (org-agenda-files org-agenda-files))) - (todo "PLAN" - ((org-agenda-overriding-header "In Planning") - (org-agenda-todo-list-sublevels nil) - (org-agenda-files org-agenda-files))) - (todo "BACKLOG" - ((org-agenda-overriding-header "Project Backlog") - (org-agenda-todo-list-sublevels nil) - (org-agenda-files org-agenda-files))) - (todo "READY" - ((org-agenda-overriding-header "Ready for Work") - (org-agenda-files org-agenda-files))) - (todo "ACTIVE" - ((org-agenda-overriding-header "Active Projects") - (org-agenda-files org-agenda-files))) - (todo "COMPLETED" - ((org-agenda-overriding-header "Completed Projects") - (org-agenda-files org-agenda-files))) - (todo "CANC" - ((org-agenda-overriding-header "Cancelled Projects") - (org-agenda-files org-agenda-files))))))) - - -#+end_src -*** org capture templates -:PROPERTIES: -:CUSTOM_ID: h:23183635-3d46-4d7d-8eda-e0a085b335ef -:END: - -I wrote these capture templates to allow myself to quickly create Anki cards from within Emacs. I nearly never use this feature, but it cannot hurt to have. - -#+begin_src emacs-lisp - - (setq org-capture-templates - `( - ("a" "Anki basic" - entry - (file+headline swarsel-org-anki-filepath "Dispatch") - (function swarsel-anki-make-template-string)) - - ("A" "Anki cloze" - entry - (file+headline org-swarsel-anki-file "Dispatch") - "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: Cloze\n:ANKI_DECK: 🦁 All::01 ❤️ Various::00 ✨ Allgemein\n:END:\n** Text\n%?\n** Extra\n") - ("t" "Tasks / Projects") - ("tt" "Task" entry (file+olp swarsel-org-tasks-filepath "Inbox") - "* TODO %?\n %U\n %a\n %i" :empty-lines 1) - )) - ) -#+end_src - -*** Font Faces -:PROPERTIES: -:CUSTOM_ID: h:40528f5a-c8cd-471b-b862-4088e8e61860 -:END: - -Again, my understanding of the font-faces in Emacs is limited. This is mostly just tuned so that my org-files look acceptable. - -#+begin_src emacs-lisp - - - - ;; Set faces for heading levels - (with-eval-after-load 'org-faces (dolist (face '((org-level-1 . 1.1) - (org-level-2 . 0.9) - (org-level-3 . 0.9) - (org-level-4 . 0.9) - (org-level-5 . 0.9) - (org-level-6 . 0.9) - (org-level-7 . 0.9) - (org-level-8 . 0.9))) - (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) - - ;; Ensure that anything that should be fixed-pitch in Org files appears that way - (set-face-attribute 'org-block nil :inherit 'fixed-pitch) - (set-face-attribute 'org-table nil :inherit 'fixed-pitch) - (set-face-attribute 'org-formula nil :inherit 'fixed-pitch) - (set-face-attribute 'org-code nil :inherit '(shadow 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)) - -#+end_src - -*** org-appear -:PROPERTIES: -:CUSTOM_ID: h:62829574-a069-44b8-afb3-401a268d2747 -:END: - -This package makes emphasis-markers appear when the cursor moves over them. Very useful as I enjoy the clean look of not always seeing them, but it is annoying not to be able to edit them properly. - -#+begin_src emacs-lisp - - (use-package org-appear - :hook (org-mode . org-appear-mode) - :init - (setq org-appear-autolinks t) - (setq org-appear-autokeywords t) - (setq org-appear-autoentities t) - (setq org-appear-autosubmarkers t)) - -#+end_src - -*** Centered org-mode Buffers -:PROPERTIES: -:CUSTOM_ID: h:bbcfa895-4d46-4b1d-b84e-f634e982c46e -:END: - -I like org-mode buffers to be centered, as I do not find that enormous lines are of big use. - -Function definition in: [[#h:fa710375-2efe-49b4-af6a-a875aca6e4a2][Visual-fill column]] - -#+begin_src emacs-lisp - - (use-package visual-fill-column - :hook (org-mode . swarsel/org-mode-visual-fill)) - -#+end_src - -*** Fix headings not folding sometimes -:PROPERTIES: -:CUSTOM_ID: h:c1a0adea-ca97-43d7-b5a0-b856d2ebc9a8 -:END: - -There is a weird bug in org-mode that makes it so that headings were not folding correctly sometimes. This setting seems to fix it. - -#+begin_src emacs-lisp - - (setq org-fold-core-style 'overlays) - -#+end_src - -*** Babel -:PROPERTIES: -:CUSTOM_ID: h:3e0b6da3-0497-4080-bb49-bab949c03bc4 -:END: - -org-babel allows to run blocks in other programming languages within an org-mode buffer, similar to what e.g. jupyterhub offers for python. - -It also offers a very useful utility of exporting org-mode buffers to different formats; the feature I enjoy most is what makes this file useful: the tangling functionality. - -**** Language Configuration -:PROPERTIES: -:CUSTOM_ID: h:5d5ed7be-ec5f-4e17-bbb8-820ab6a9961c -:END: - -- This configures the languages that babel recognizes. - -#+begin_src emacs-lisp - - (org-babel-do-load-languages - 'org-babel-load-languages - '((emacs-lisp . t) - (python . t) - (shell . t) - )) - - (push '("conf-unix" . conf-unix) org-src-lang-modes) - -#+end_src - -**** old easy structure templates -:PROPERTIES: -:CUSTOM_ID: h:d112ed66-b2dd-45cc-8d70-9cf6631f28a9 -:END: - -- org 9.2 changed the way structure templates work. This brings back the old way it worked. - - Usage: Type =<=, followed by one of the below keywords and press =RET=. The corresponding source block should appear. - - #+begin_src emacs-lisp - - (require 'org-tempo) - (add-to-list 'org-structure-template-alist '("sh" . "src shell")) - (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) - (add-to-list 'org-structure-template-alist '("py" . "src python :results output")) - (add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle")) - - #+end_src - -*** aucTex -:PROPERTIES: -:CUSTOM_ID: h:4696e2fc-3296-47dc-8fc3-66912c329d4c -:END: - -This provides several utilities for LaTeX in Emacs, including many completions and convenience functions for math-mode. - -#+begin_src emacs-lisp - - (use-package auctex) - (setq TeX-auto-save t) - (setq TeX-save-query nil) - (setq TeX-parse-self t) - (setq-default TeX-master nil) - - (add-hook 'LaTeX-mode-hook 'visual-line-mode) - (add-hook 'LaTeX-mode-hook 'flyspell-mode) - (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode) - (add-hook 'LaTeX-mode-hook 'reftex-mode) - (setq LaTeX-electric-left-right-brace t) - (setq font-latex-fontify-script nil) - (setq TeX-electric-sub-and-superscript t) - ;; (setq reftex-plug-into-AUCTeX t) - -#+end_src - -*** org-download -:PROPERTIES: -:CUSTOM_ID: h:406e5ecb-66f0-49bf-85ca-8b499f73ec5b -:END: - -This package allows to download and copy images into org-mode buffers. Sadly it does not work in a very stable manner - if you copy images that are also links to another page (like is often the case in a Google image search), Emacs might crash from this. - -#+begin_src emacs-lisp - - (use-package org-download - :after org - :defer nil - :custom - (org-download-method 'directory) - (org-download-image-dir "./images") - (org-download-heading-lvl 0) - (org-download-timestamp "org_%Y%m%d-%H%M%S_") - ;;(org-image-actual-width 500) - (org-download-screenshot-method "grim -g \"$(slurp)\" %s") - :bind - ("C-M-y" . org-download-screenshot) - :config - (require 'org-download)) - -#+end_src - -*** org-fragtog -:PROPERTIES: -:CUSTOM_ID: h:a02b1162-3e19-46f1-8efc-9f375971645c -:END: - -This package automatically toggles LaTeX-fragments in org-files. It seems to also work in markdown-files which is a nice addition, as my Obsidian notes are held in markdown. - -#+begin_src emacs-lisp - - (use-package org-fragtog) - (add-hook 'org-mode-hook 'org-fragtog-mode) - (add-hook 'markdown-mode-hook 'org-fragtog-mode) - -#+end_src - -*** org-modern -:PROPERTIES: -:CUSTOM_ID: h:95b42e77-767c-4461-9ba8-b1c1cd18266c -:END: - -This just makes org-mode a little bit more beautiful, mostly by making the =begin_src= and =end_src= tags in source-blocks turn into more beautiful icons, as well as hiding =#+= tags before them, as well as in the properties section of the file. - -#+begin_src emacs-lisp - - (use-package org-modern - :config (setq org-modern-block-name - '((t . t) - ("src" "»" "∥"))) - :hook (org-mode . org-modern-mode)) - -#+end_src - -*** Presentations -:PROPERTIES: -:CUSTOM_ID: h:4e11a845-a7bb-4eb5-b4ce-5b2f52e07425 -:END: - -Recently I have grown fond of holding presentations using Emacs :) - -#+begin_src emacs-lisp - - (use-package org-present - :bind (:map org-present-mode-keymap - ("q" . org-present-quit) - ("" . swarsel/org-present-prev) - ("" . 'ignore) - ("" . 'ignore) - ("" . swarsel/org-present-next)) - :hook ((org-present-mode . swarsel/org-present-start) - (org-present-mode-quit . swarsel/org-present-end)) - ) - - - (use-package hide-mode-line) - - (defun swarsel/org-present-start () - (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-verbatim (:height 1.55) org-verbatim) - (org-block (:height 1.25) org-block) - (org-block-begin-line (:height 0.7) org-block) - )) - (dolist (face '((org-level-1 . 1.1) - (org-level-2 . 1.2) - (org-level-3 . 1.2) - (org-level-4 . 1.2) - (org-level-5 . 1.2) - (org-level-6 . 1.2) - (org-level-7 . 1.2) - (org-level-8 . 1.2))) - (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) - - (setq header-line-format " ") - (setq visual-fill-column-width 90) - (setq indicate-buffer-boundaries nil) - (setq inhibit-message nil) - (breadcrumb-mode 0) - (org-display-inline-images) - (global-hl-line-mode 0) - (display-line-numbers-mode 0) - (org-modern-mode 0) - (evil-insert-state 1) - (beginning-of-buffer) - (org-present-read-only) - ;; (org-present-hide-cursor) - (swarsel/org-present-slide) - ) - - (defun swarsel/org-present-end () - (setq-local face-remapping-alist '((default variable-pitch default))) - (dolist (face '((org-level-1 . 1.1) - (org-level-2 . 0.9) - (org-level-3 . 0.9) - (org-level-4 . 0.9) - (org-level-5 . 0.9) - (org-level-6 . 0.9) - (org-level-7 . 0.9) - (org-level-8 . 0.9))) - (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) - (setq header-line-format nil) - (setq visual-fill-column-width 150) - (setq indicate-buffer-boundaries t) - (setq inhibit-message nil) - (breadcrumb-mode 1) - (global-hl-line-mode 1) - (display-line-numbers-mode 1) - (org-remove-inline-images) - (org-modern-mode 1) - (evil-normal-state 1) - ;; (org-present-show-cursor) - ) - - (defun swarsel/org-present-slide () - (org-overview) - (org-show-entry) - (org-show-children) - ) - - (defun swarsel/org-present-prev () - (interactive) - (org-present-prev) - (swarsel/org-present-slide)) - - (defun swarsel/org-present-next () - (interactive) - (unless (eobp) - (org-next-visible-heading 1) - (org-fold-show-entry)) - (when (eobp) - (org-present-next) - (swarsel/org-present-slide) - )) - - (defun clojure-leave-clojure-mode-function () - ) - - (add-hook 'buffer-list-update-hook #'clojure-leave-clojure-mode-function) - (add-hook 'org-present-mode-hook 'swarsel/org-present-start) - (add-hook 'org-present-mode-quit-hook 'swarsel/org-present-end) - (add-hook 'org-present-after-navigate-functions 'swarsel/org-present-slide) - -#+end_src -** Nix Mode -:PROPERTIES: -:CUSTOM_ID: h:406c2ecc-0e3e-4d9f-9ae3-3eb1f8b87d1b -:END: - -This adds a rudimentary nix-mode to Emacs. I have not really tried this out, as I am mostly editing nix-files in org-mode anyways. - -#+begin_src emacs-lisp - - (use-package nix-mode - :mode "\\.nix\\'") - -#+end_src - -** Markdown Mode -:PROPERTIES: -:CUSTOM_ID: h:50327461-a11b-4e81-830a-90febc720cfa -:END: -*** Mode -:PROPERTIES: -:CUSTOM_ID: h:734dc40a-a2c4-4839-b884-cb99b81aa6fe -:END: - -#+begin_src emacs-lisp - - (setq markdown-command "pandoc") - - (use-package markdown-mode - :ensure t - :mode ("README\\.md\\'" . gfm-mode) - :init (setq markdown-command "multimarkdown") - :bind (:map markdown-mode-map - ("C-c C-e" . markdown-do))) - -#+end_src - -*** LaTeX in Markdown -:PROPERTIES: -:CUSTOM_ID: h:8d90fe51-0b32-423a-a159-4f853bc29b68 -:END: - -#+begin_src emacs-lisp - - (add-hook 'markdown-mode-hook - (lambda () - (local-set-key (kbd "C-c C-x C-l") 'org-latex-preview) - (local-set-key (kbd "C-c C-x C-u") 'markdown-toggle-url-hiding) - )) - -#+end_src - -** Olivetti -:PROPERTIES: -:CUSTOM_ID: h:65e69741-9860-4ed0-bbed-7b7be9a2a9d6 -:END: - -Olivetti is a mode specialized for writing prose in Emacs. I went for a very simple setup with little distractions. - -This mode is not automatically activated anywhere because I only rarely need it. - -#+begin_src emacs-lisp - - (use-package olivetti - :init - (setq olivetti-body-width 100) - (setq olivetti-recall-visual-line-mode-entry-state t)) - -#+end_src - -** darkroom -:PROPERTIES: -:CUSTOM_ID: h:94d4a0dc-b0d7-4702-b760-beeaa6da2b8f -:END: - -Darkroom is package that reduces all forms of distraction to a minimum - this can be useful when simply reading a file for example. For this mode I have increased the text scale by a large margin to make for comfortable reading -This mode is not automatically activated anywhere because I only rarely need it. - -#+begin_src emacs-lisp - -(use-package darkroom - :init - (setq darkroom-text-scale-increase 3)) - -#+end_src - -** Ripgrep -:PROPERTIES: -:CUSTOM_ID: h:87453f1c-8ea5-4d0a-862d-8973d5bc5405 -:END: - -This is the ripgrep command for Emacs. - -#+begin_src emacs-lisp - - (use-package rg) - -#+end_src -** Tree-sitter -:PROPERTIES: -:CUSTOM_ID: h:543641d0-02a9-459e-a2d6-96c8fcc06864 -:END: - -Tree-sitter is a parsing library integrated into Emacs to provide better syntax highlighting and code analysis. It generates concrete syntax trees for source code, enabling more accurate and efficient text processing. Emacs' tree-sitter integration enhances language support, offering features like incremental parsing and precise syntax-aware editing. This improves the development experience by providing robust and dynamic syntax features, making it easier for me to navigate and manipulate code. - -In order to update the language grammars, run the next command below. - -#+begin_src emacs-lisp :tangle no :export both - - (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist)) - -#+end_src - -#+RESULTS: -| bash | c | cmake | cpp | css | elisp | go | html | javascript | json | julia | latex | make | markdown | R | python | typescript | rust | sql | toml | tsx | yaml | - -#+begin_src emacs-lisp - - (use-package emacs - :ensure nil - :init - (setq treesit-language-source-alist - '((bash . ("https://github.com/tree-sitter/tree-sitter-bash")) - (c . ("https://github.com/tree-sitter/tree-sitter-c")) - (cmake . ("https://github.com/uyha/tree-sitter-cmake")) - (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp")) - (css . ("https://github.com/tree-sitter/tree-sitter-css")) - (elisp . ("https://github.com/Wilfred/tree-sitter-elisp")) - (go . ("https://github.com/tree-sitter/tree-sitter-go")) - (html . ("https://github.com/tree-sitter/tree-sitter-html")) - (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript")) - (json . ("https://github.com/tree-sitter/tree-sitter-json")) - (julia . ("https://github.com/tree-sitter/tree-sitter-julia")) - (latex . ("https://github.com/latex-lsp/tree-sitter-latex")) - (make . ("https://github.com/alemuller/tree-sitter-make")) - (markdown . ("https://github.com/ikatyang/tree-sitter-markdown")) - (R . ("https://github.com/r-lib/tree-sitter-r")) - (python . ("https://github.com/tree-sitter/tree-sitter-python")) - (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "typescript/src" "typescript")) - (rust . ("https://github.com/tree-sitter/tree-sitter-rust")) - (sql . ("https://github.com/m-novikov/tree-sitter-sql")) - (toml . ("https://github.com/tree-sitter/tree-sitter-toml")) - (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")) - (yaml . ("https://github.com/ikatyang/tree-sitter-yaml")))) - ) - - (use-package treesit-auto - :config - (global-treesit-auto-mode) - (setq treesit-auto-install 'prompt)) - - -#+end_src - -** direnv -:PROPERTIES: -:CUSTOM_ID: h:82ddeef2-99f8-465b-ba36-07c3eaad717b -:END: - -#+begin_src emacs-lisp - - (use-package direnv - :custom (direnv-always-show-summary nil) - :config (direnv-mode)) - -#+end_src - -** avy -:PROPERTIES: -:CUSTOM_ID: h:efb3f0fd-e846-4df9-ba48-2e45d776f68f -:END: - -=avy= provides the ability to search for any character on the screen (not only in the current buffer!) - I enjoy this utility a lot and use it possibly even more often than the native vim commands. - -#+begin_src emacs-lisp - - (use-package avy - :bind - (("M-o" . avy-goto-char-timer)) - :config - (setq avy-all-windows 'all-frames)) - -#+end_src - -** crdt (Collaborative Editing) -:PROPERTIES: -:CUSTOM_ID: h:1c1821c6-98de-4079-a4f3-6ba6e6dcb668 -:END: - -With this it is possible to work on the same file collaboratively. I have never tried it out, but it sounds cool. - -#+begin_src emacs-lisp - -(use-package crdt) - -#+end_src - -** devdocs -:PROPERTIES: -:CUSTOM_ID: h:d9a6cb44-736e-4608-951f-e928e1b757c0 -:END: - -=devdocs= is a very nice package that provides documentation from [[https:devdocs.io]]. This is very useful since e.g. =pyright= provides only a very bad documentation and I do not want to leave Emacs all the time just to read documentation. - -To install a documentation, use the =devdocs=install= command and select the appropriate version. =devdocs-update-all= can be used to download and reinstall all installed documents if a newer version is available. Check documentation with =devdocs-lookup= (=C-SPC h d=). - -#+begin_src emacs-lisp - - (use-package devdocs) - - (add-hook 'python-mode-hook - (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1")))) - (add-hook 'python-ts-mode-hook - (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1")))) - - (add-hook 'c-mode-hook - (lambda () (setq-local devdocs-current-docs '("c")))) - (add-hook 'c-ts-mode-hook - (lambda () (setq-local devdocs-current-docs '("c")))) - - (add-hook 'c++-mode-hook - (lambda () (setq-local devdocs-current-docs '("cpp")))) - (add-hook 'c++-ts-mode-hook - (lambda () (setq-local devdocs-current-docs '("cpp")))) - - (devdocs-update-all) - -#+end_src - -** Projectile -:PROPERTIES: -:CUSTOM_ID: h:5cde5032-251e-4cc4-9202-b4ce996f92c2 -:END: - -projectile is useful for keeping track of your git projects within Emacs. I mostly use it to quickly switch between projects. - -#+begin_src emacs-lisp - - (use-package projectile - :diminish projectile-mode - :config (projectile-mode) - :custom ((projectile-completion-system 'auto)) ;; integrate ivy into completion system - :bind-keymap - ("C-c p" . projectile-command-map) ; all projectile commands under this - :init - ;; NOTE: Set this to the folder where you keep your Git repos! - (when (file-directory-p swarsel-projects-directory) - (setq projectile-project-search-path (list swarsel-projects-directory))) - (setq projectile-switch-project-action #'magit-status)) - -#+end_src - -** Magit -:PROPERTIES: -:CUSTOM_ID: h:d2c7323d-f8c6-4f23-b70a-930e3e4ecce5 -:END: - -magit is the best git utility I have ever used - it has a beautiful interface and is very verbose. Here I mostly just setup the list of repositories that I want to expost to magit. - -Also, Emacs needs a little extra love to accept my Yubikey for git commits etc. We also set that here. - -#+begin_src emacs-lisp - - (use-package magit - :config - (setq magit-repository-directories `((,swarsel-projects-directory . 1) - (,swarsel-emacs-directory . 0) - (,swarsel-obsidian-directory . 0) - ("~/.dotfiles/" . 0))) - :custom - (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ; stay in the same window - - ;; yubikey support for pushing commits - ;; commiting is enabled through nixos gpg-agent config - (setq epg-pinentry-mode 'loopback) - (setenv "SSH_AUTH_SOCK" (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket"))) -#+end_src - -** Forge -:PROPERTIES: -:CUSTOM_ID: h:1a8585ed-d9f2-478f-a132-440ada1cde2c -:END: - -NOTE: Make sure to configure a GitHub token before using this package! -- https://magit.vc/manual/forge/Token-Creation.html#Token-Creation -- https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started -- https://magit.vc/manual/ghub/Storing-a-Token.html -- https://www.emacswiki.org/emacs/GnuPG - - (1) in practice: github -<> settings -<> developer option -<> - create classic token with repo; user; read:org permissions - (2) install GnuGP (and add to PATH) - (3) create ~/.authinfo.gpg with the following info scheme: - machine api.github.com login USERNAME^forge password 012345abcdef... - -#+begin_src emacs-lisp - - (use-package forge - :after magit) - - (with-eval-after-load 'forge - (add-to-list 'forge-alist - '("sgit.iue.tuwien.ac.at" - "sgit.iue.tuwien.ac.at/api/v1" - "sgit.iue.tuwien.ac.at" - forge-gitea-repository))) -#+end_src - -** git-timemachine -:PROPERTIES: -:CUSTOM_ID: h:cf5b0e6b-56a5-4a93-99fb-258eb7cb2eb4 -:END: - -This is just a nice utility to browse different versions of a file of a git project within Emacs. - -#+begin_src emacs-lisp - - (use-package git-timemachine - :hook (git-time-machine-mode . evil-normalize-keymaps) - :init (setq git-timemachine-show-minibuffer-details t)) - -#+end_src - -** Delimiters (brackets): rainbow-delimiters, highlight-parentheses -:PROPERTIES: -:CUSTOM_ID: h:d9671ab7-a75a-47c6-a1f4-376d126c9b0a -:END: - -- rainbow-delimiters colors all delimiters, also ones not in current selection -- paren highlights the current delimiter selection especially bold -- highlight-parentheses boldly highlights all delimiters in current selection - -I am not completely sure on electric-pair-mode yet, sometimes it is very helpful, sometimes it annoys me to no end. - -#+begin_src emacs-lisp - - (use-package rainbow-delimiters - :hook (prog-mode . rainbow-delimiters-mode)) - - (use-package highlight-parentheses - :config - (setq highlight-parentheses-colors '("black" "white" "black" "black" "black" "black" "black")) - (setq highlight-parentheses-background-colors '("magenta" "blue" "cyan" "green" "yellow" "orange" "red")) - (global-highlight-parentheses-mode t)) - - (electric-pair-mode 1) - (setq electric-pair-preserve-balance nil) - ;; don't try to be overly smart - (setq electric-pair-delete-adjacent-pairs nil) - ;; don't skip newline when auto-pairing parenthesis - (setq electric-pair-skip-whitespace-chars '(9 32)) - - ;; in org-mode buffers, do not pair < and > in order not to interfere with org-tempo - (add-hook 'org-mode-hook (lambda () - (setq-local electric-pair-inhibit-predicate - `(lambda (c) - (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c)))))) - -#+end_src - -** rainbow-mode -:PROPERTIES: -:CUSTOM_ID: h:d1a32a69-2f9a-45ef-95fe-a00e3551dc94 -:END: - -Complimentary to the delimiters-packages above, this package sets the background color of the delimiters, which makes it easier to see at a glance where we are in a delimiter-tree. - -#+begin_src emacs-lisp - - (use-package rainbow-mode - :config (rainbow-mode)) - -#+end_src -** Corfu -:PROPERTIES: -:CUSTOM_ID: h:5653d693-ecca-4c95-9633-66b9e3241070 -:END: - -This is the company equivalent to the vertico gang. -I dislike the standard behaviour that makes the cursor move into the completion framework on presses of == and ==. - -Nerd icons is originally enabled here: [[#h:eb0ea526-a83a-4664-b3a1-2b40d3a31493][Icons]] - -Navigation functions defined here: [[#h:a1802f9b-bb71-4fd5-86fa-945da18e8b81][corfu: Do not interrupt navigation]] - -#+begin_src emacs-lisp - - ;; (use-package corfu - ;; :custom - ;; (corfu-cycle t) - ;; :init - ;; (global-corfu-mode)) - - (use-package corfu - :init - (global-corfu-mode) - (corfu-history-mode) - (corfu-popupinfo-mode) ; Popup completion info - :custom - (corfu-auto t) - (corfu-auto-prefix 3) - (corfu-auto-delay 0.3) - (corfu-cycle t) - (corfu-quit-no-match 'separator) - (corfu-separator ?\s) - ;; (corfu-quit-no-match t) - (corfu-popupinfo-max-height 70) - (corfu-popupinfo-delay '(0.5 . 0.2)) - ;; (corfu-preview-current 'insert) ; insert previewed candidate - (corfu-preselect 'prompt) - (corfu-on-exact-match nil) ; Don't auto expand tempel snippets - ;; Optionally use TAB for cycling, default is `corfu-complete'. - :bind (:map corfu-map - ("M-SPC" . corfu-insert-separator) - ("" . swarsel/corfu-normal-return) - ;; ("C-" . swarsel/corfu-complete) - ("S-" . corfu-popupinfo-scroll-down) - ("S-" . corfu-popupinfo-scroll-up) - ("C-" . corfu-previous) - ("C-" . corfu-next) - (" " . swarsel/corfu-quit-and-up) - (" " . swarsel/corfu-quit-and-down)) - ) - - (use-package nerd-icons-corfu) - - (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter) - - (setq nerd-icons-corfu-mapping - '((array :style "cod" :icon "symbol_array" :face font-lock-type-face) - (boolean :style "cod" :icon "symbol_boolean" :face font-lock-builtin-face) - ;; ... - (t :style "cod" :icon "code" :face font-lock-warning-face))) - -#+end_src - -** cape -:PROPERTIES: -:CUSTOM_ID: h:c3cc1c12-3ab8-42b7-be07-63f54eac397f -:END: - -cape adds even more completion capabilities by adding a lot of completion logic that is exposed as separate functions. I tried out adding these to the =completion-at-points-functions= alist, but I felt like it cluttered my suggestions too much. Hence I now just call the respective functions when I need them. For this I setup the =C-z= keybinding in [[#h:218376e8-086b-46bf-91b3-78295d5d440f][General evil]]. - -I leave the commented out alist extensions here in case I want to try them out at some point in the future. - -#+begin_src emacs-lisp - - (use-package cape - :bind - ("C-z p" . completion-at-point) ;; capf - ("C-z t" . complete-tag) ;; etags - ("C-z d" . cape-dabbrev) ;; or dabbrev-completion - ("C-z h" . cape-history) - ("C-z f" . cape-file) - ("C-z k" . cape-keyword) - ("C-z s" . cape-elisp-symbol) - ("C-z e" . cape-elisp-block) - ("C-z a" . cape-abbrev) - ("C-z l" . cape-line) - ("C-z w" . cape-dict) - ("C-z :" . cape-emoji) - ("C-z \\" . cape-tex) - ("C-z _" . cape-tex) - ("C-z ^" . cape-tex) - ("C-z &" . cape-sgml) - ("C-z r" . cape-rfc1345) - ;; Add to the global default value of `completion-at-point-functions' which is - ;; used by `completion-at-point'. The order of the functions matters, the - ;; first function returning a result wins. Note that the list of buffer-local - ;; completion functions takes precedence over the global list. - ;; (add-to-list 'completion-at-point-functions #'cape-dabbrev) - ;; (add-to-list 'completion-at-point-functions #'cape-file) - ;; (add-to-list 'completion-at-point-functions #'cape-elisp-block) - ;; (add-to-list 'completion-at-point-functions #'cape-history) - ;; (add-to-list 'completion-at-point-functions #'cape-keyword) - ;; (add-to-list 'completion-at-point-functions #'cape-tex) - ;; (add-to-list 'completion-at-point-functions #'cape-sgml) - ;; (add-to-list 'completion-at-point-functions #'cape-rfc1345) - ;; (add-to-list 'completion-at-point-functions #'cape-abbrev) - ;; (add-to-list 'completion-at-point-functions #'cape-dict) - ;; (add-to-list 'completion-at-point-functions #'cape-elisp-symbol) - ;; (add-to-list 'completion-at-point-functions #'cape-line) - ) - -#+end_src - -** rust -:PROPERTIES: -:CUSTOM_ID: h:3aa20438-edf6-4b13-a90d-3d5c51239c44 -:END: - -This is supposed to setup a rust-lsp - however, this has not worked nicely in the past and this configuration section is just a ruin really. I need to check what works and clean this up. - -#+begin_src emacs-lisp - - ;; (use-package rustic - ;; :ensure - ;; :bind (:map rustic-mode-map - ;; ("M-j" . lsp-ui-imenu) - ;; ("M-?" . lsp-find-references) - ;; ("C-c C-c l" . flycheck-list-errors) - ;; ("C-c C-c a" . lsp-execute-code-action) - ;; ("C-c C-c r" . lsp-rename) - ;; ("C-c C-c q" . lsp-workspace-restart) - ;; ("C-c C-c Q" . lsp-workspace-shutdown) - ;; ("C-c C-c s" . lsp-rust-analyzer-status)) - ;; :config - - ;; (setq rustic-format-on-save t) - ;; (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook)) - - ;; (defun rk/rustic-mode-hook () - ;; ;; so that run C-c C-c C-r works without having to confirm, but don't try to - ;; ;; save rust buffers that are not file visiting. Once - ;; ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should - ;; ;; no longer be necessary. - ;; (when buffer-file-name - ;; (setq-local buffer-save-without-query t)) - ;; (add-hook 'before-save-hook 'lsp-format-buffer nil t)) - - ;; (use-package rustic - ;; :config - ;; (setq rustic-format-on-save t) - ;; (setq rustic-lsp-client 'eglot) - ;; :custom - ;; (lsp-rust-analyzer-cargo-watch-command "clippy") - ;; (lsp-rust-analyzer-server-display-inlay-hints t) - ;; :mode ("\\.rs" . rustic-mode)) - - (use-package rustic - :config - (setq rustic-format-on-save t) - (setq rustic-lsp-client 'eglot) - :mode ("\\.rs" . rustic-mode)) -#+end_src - -** Tramp -:PROPERTIES: -:CUSTOM_ID: h:b9b27a88-06f3-470b-a604-a20b2079bc26 -:END: - -Tramp allows for SSH access of files over Emacs. I have no ideas what the options here mean, but this is a recommended configuration that I found (sadly I lost the link). I need to research more what these options really do. - -#+begin_src emacs-lisp - - - -(use-package tramp - :init - (setq vc-ignore-dir-regexp - (format "\\(%s\\)\\|\\(%s\\)" - vc-ignore-dir-regexp - tramp-file-name-regexp)) - (setq tramp-default-method "ssh") - (setq tramp-auto-save-directory - (expand-file-name "tramp-auto-save" user-emacs-directory)) - (setq tramp-persistency-file-name - (expand-file-name "tramp-connection-history" user-emacs-directory)) - (setq password-cache-expiry nil) - (setq tramp-use-ssh-controlmaster-options nil) - (setq remote-file-name-inhibit-cache nil) - :config - (customize-set-variable 'tramp-ssh-controlmaster-options - (concat - "-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p " - "-o ControlMaster=auto -o ControlPersist=yes")) -) - - - -#+end_src - -** diff-hl -:PROPERTIES: -:CUSTOM_ID: h:58415e95-8a7a-4517-acbb-5f1bb1028603 -:END: - -This is a simple highlighting utility that uses the margin to visually show the differences since the last git commit. - -#+begin_src emacs-lisp - - (use-package diff-hl - :hook - ((prog-mode - org-mode) . diff-hl-mode) - :init - (diff-hl-flydiff-mode) - (diff-hl-margin-mode) - (diff-hl-show-hunk-mouse-mode)) - -#+end_src -** Commenting -:PROPERTIES: -:CUSTOM_ID: h:d60ce0b1-cabf-43f5-a236-a1e4b400d2f5 -:END: - -This package allows for swift commenting out and in of code snippets. For some reason, it is a bit broken in my config, as it sometimes comments out too much, sometimes too little, and sometimes it splits lines during commenting. Also, in org-mode when inside a src-block, it often times jumps to the top of the block. - -Still, this is avery convenient package. - -#+begin_src emacs-lisp - - (use-package evil-nerd-commenter - :bind ("M-/" . evilnc-comment-or-uncomment-lines)) - -#+end_src - -** yasnippet -:PROPERTIES: -:CUSTOM_ID: h:9ec11ee4-2250-414a-87b5-73ee680a3a4a -:END: - -yasnippet allows to define snippets that can be quickly expanded by hitting the =TAB= key after inputting a keyword. - -I used to run this together with the =yasnippet-snippets= package, but the snippets in there I did not find all too useful for myself. I need to create some custom snippets here one day. - -#+begin_src emacs-lisp - - (use-package yasnippet - :init (yas-global-mode 1) - :config - (yas-reload-all)) - -#+end_src - -**** yasnippet math-snippets - -The following block is mostly inspired from [[https://code.kulupu.party/thesuess/WTFmacs/]] and sets up a few prefixes that make LaTeX-math-mode nicer to use even with auctex and cape enabled. - -#+begin_src emacs-lisp - - - (setq wtf/latex-mathbb-prefix "''") - (setq swarsel/latex-mathcal-prefix "``") - - (use-package yasnippet - :config - - (setq wtf/english-alphabet - '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z")) - - (dolist (elem wtf/english-alphabet) - (when (string-equal elem (downcase elem)) - (add-to-list 'wtf/english-alphabet (upcase elem)))) - - - (yas-define-snippets - 'latex-mode - (mapcar - (lambda (elem) - (list (concat wtf/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem))) - wtf/english-alphabet)) - - (yas-define-snippets - 'latex-mode - (mapcar - (lambda (elem) - (list (concat swarsel/latex-mathcal-prefix elem) (concat "\\mathcal{" elem "}") (concat "Mathcal letter " elem))) - wtf/english-alphabet)) - - (setq swtf/latex-math-symbols - '(("x" . "\\times") - ("*" . "\\cdot") - ("." . "\\ldots") - ("op" . "\\operatorname{$1}$0") - ("o" . "\\circ") - ("V" . "\\forall") - ("v" . "\\vee") - ("w" . "\\wedge") - ("q" . "\\quad") - ("f" . "\\frac{$1}{$2}$0") - ("s" . "\\sum_{$1}^{$2}$0") - ("p" . "\\prod_{$1}^{$2}$0") - ("e" . "\\exists") - ("i" . "\\int_{$1}^{$2}$0") - ("c" . "\\cap") - ("u" . "\\cup") - ("0" . "\\emptyset"))) - - ) - - -#+end_src - -** eglot - -After having tried out =lsp-mode= and =lsp-bridge= for a while each, I must say that =eglot= feels the most clean and fast to me. Rust-modes need a little extra care to get working here. - -:PROPERTIES: -:CUSTOM_ID: h:424fbc62-84e2-42c7-a1ca-e43ea04c43e5 -:END: - -#+begin_src emacs-lisp - - (use-package eglot - :ensure nil - :hook - ((python-mode - python-ts-mode - c-mode - c-ts-mode - c++-mode - c++-ts-mode - tex-mode - LaTeX-mode - ) . (lambda () (progn - (eglot-ensure) - (add-hook 'before-save-hook 'eglot-format nil 'local)))) - :custom - (eldoc-echo-area-use-multiline-p nil) - (completion-category-defaults nil) - :config - ;; (push '(rustic-ts-mode . eglot-rust-analyzer) eglot-server-programs) - (push '(rustic-mode . eglot-rust-analyzer) eglot-server-programs) - (add-to-list 'eglot-server-programs '((rust-mode) . (eglot-rust-analyzer "rust-analyzer"))) - ;; (add-to-list 'eglot-server-programs '((python-mode) . ("pylsp"))) - ;; (add-to-list 'eglot-server-programs '((c-mode) . ("clangd"))) - :bind (:map eglot-mode-map - ("M-(" . flymake-goto-next-error) - ("C-c ," . eglot-code-actions))) - - (defalias 'start-lsp-server #'eglot) - -#+end_src - -** Breadcrumb - -This simple shows the path to the current file on the top of the buffer - I just think it looks kind of neat, even though it is not extremely useful :) - -#+begin_src emacs-lisp - - (use-package breadcrumb - :config (breadcrumb-mode)) - -#+end_src - -** Prevent breaking of hardlinks -:PROPERTIES: -:CUSTOM_ID: h:e9a30d0f-423f-4e85-af4b-f8560f1c1b53 -:END: - -This setting ensures that hard links are preserved during the backup process, which is useful for maintaining the integrity of files that are linked in multiple locations. - -#+begin_src emacs-lisp - - (setq backup-by-copying-when-linked t) - -#+end_src - -** Dirvish -:PROPERTIES: -:CUSTOM_ID: h:0918557a-8463-430c-b8df-6546dea9abd0 -:END: - -Dirvish is an improvement upon the dired-framework and has more features like file preview etc. Sadly it has an incompatibility with =openwith= which is why I have disabled that package. - -#+begin_src emacs-lisp - - (use-package dirvish - :init - (dirvish-override-dired-mode) - :config - (dirvish-peek-mode) - (dirvish-side-follow-mode) - (setq dirvish-open-with-programs - (append dirvish-open-with-programs '( - (("xlsx" "docx" "doc" "odt" "ods") "libreoffice" "%f") - (("jpg" "jpeg" "png") "imv" "%f") - (("pdf") "sioyek" "%f") - (("xopp") "xournalpp" "%f")))) - :custom - (delete-by-moving-to-trash t) - (dired-listing-switches - "-l --almost-all --human-readable --group-directories-first --no-group") - (dirvish-attributes - '(vc-state subtree-state nerd-icons collapse file-time file-size)) - (dirvish-quick-access-entries - '(("h" "~/" "Home") - ("c" "~/.dotfiles/" "Config") - ("d" "~/Downloads/" "Downloads") - ("D" "~/Documents/" "Documents") - ("p" "~/Documents/GitHub/" "Projects") - ("/" "/" "Root"))) - :bind - ((" d" . 'dirvish) - ("C-=" . 'dirvish-side) - :map dirvish-mode-map - ("h" . dired-up-directory) - ("" . dired-up-directory) - ("l" . dired-find-file) - ("" . dired-find-file) - ("j" . evil-next-visual-line) - ("k" . evil-previous-visual-line) - ("a" . dirvish-quick-access) - ("f" . dirvish-file-info-menu) - ("z" . dirvish-history-last) - ("J" . dirvish-history-jump) - ("y" . dirvish-yank-menu) - ("/" . dirvish-narrow) - ("TAB" . dirvish-subtree-toggle) - ("M-f" . dirvish-history-go-forward) - ("M-b" . dirvish-history-go-backward) - ("M-l" . dirvish-ls-switches-menu) - ("M-m" . dirvish-mark-menu) - ("M-t" . dirvish-layout-toggle) - ("M-s" . dirvish-setup-menu) - ("M-e" . dirvish-emerge-menu) - ("M-j" . dirvish-fd-jump))) - -#+end_src -** pdf-tools: pdf-viewer and support for dirvish -:PROPERTIES: -:CUSTOM_ID: h:b108dd3e-f34d-4ed3-98df-0bf9de055889 -:END: - -This enables pdf-previewing in dirvish and gives a much better pdf-viewer than is shipped normally by emacs. - -#+begin_src emacs-lisp - - (use-package pdf-tools - :init - (if (not (boundp 'pdf-tools-directory)) - (pdf-tools-install)) - :mode ("\\.pdf" . pdf-view-mode)) - -#+end_src - -** Jupyter -:PROPERTIES: -:CUSTOM_ID: h:c15efae7-b884-4c97-8367-ccc7e7ed9ba8 -:END: - -This is a jupyter client. Using it is a bit cumbersome though, so I have not fully explored all features. - -#+begin_src emacs-lisp - - (use-package ein) - -#+end_src - -** undo-tree -:PROPERTIES: -:CUSTOM_ID: h:1fc538d1-8c53-48b2-8652-66046f4bbbf8 -:END: - -Base emacs undo logic is very useful, but not easy to understand for me. I prefer undo-tree, which makes switching between branches easier and also allows quickly switching back to a much older state using the visualizer. - -Evil needs to be told to use this mode, see =(evil-set-undo-system 'undo-tree)= in [[#h:218376e8-086b-46bf-91b3-78295d5d440f][Evil/General.]] - -By default, I am not using undo-tree-mode in every buffer. This might change in the future, but for now this is fine. It can be enabled manually should the need arise. - -While we are at it, we are also setting up a persistent undo-file for every file that we are working with. - -#+begin_src emacs-lisp - - (use-package undo-tree - ;; :init (global-undo-tree-mode) - :bind (:map undo-tree-visualizer-mode-map - ("h" . undo-tree-visualize-switch-branch-left) - ("l" . undo-tree-visualize-switch-branch-left) - ("j" . undo-tree-visualize-redo) - ("k" . undo-tree-visualize-undo)) - :config - (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) - - (add-hook 'prog-mode-hook 'undo-tree-mode) - (add-hook 'text-mode-hook 'undo-tree-mode) - (add-hook 'org-mode-hook 'undo-tree-mode) - (add-hook 'latex-mode-hook 'undo-tree-mode) -#+end_src -** Hydra -:PROPERTIES: -:CUSTOM_ID: h:b6c18dd0-3377-47ea-80c3-ac1486454e18 -:END: - -Hydra allows for the writing of macro-style functions. I have not yet looked into this all too much, but it seems to be a potent feature. - -#+begin_src emacs-lisp - - (use-package hydra) - -#+end_src - -*** Text scaling -:PROPERTIES: -:CUSTOM_ID: h:c5681884-7040-4b55-ab1b-5777631a0514 -:END: - -I only wrote this in order to try out hydra; rarely do I really need this. However, it can be useful for [[#h:4e11a845-a7bb-4eb5-b4ce-5b2f52e07425][Presentations]]. It simply scales the text size. - -#+begin_src emacs-lisp - - - ;; change the text size of the current buffer - (defhydra hydra-text-scale (:timeout 4) - "scale text" - ("j" text-scale-increase "in") - ("k" text-scale-decrease "out") - ("f" nil "finished" :exit t)) - -#+end_src -** External Applications -:PROPERTIES: -:CUSTOM_ID: h:fff816a0-6d70-4bda-abab-833345e51100 -:END: -*** Obsidian -:PROPERTIES: -:CUSTOM_ID: h:9335d32d-bf08-4601-820d-f3d1f33f876f -:END: - -This provides an interface to Obsidian for Emacs - as much as I want to like it, I actually enjoy using the official Obsidian app more - even though that cannot be used by Emacs directly. - -My workflow for Obsidian is now as follows: - -1) create notes either in Emacs or Obsidian -2) look at them in the official client - - I hope that this package will improve, then I will come back to it one day. - -#+begin_src emacs-lisp - - ;; (use-package obsidian - ;; :ensure t - ;; :demand t - ;; :config - ;; (obsidian-specify-path swarsel-obsidian-vault-directory) - ;; (global-obsidian-mode t) - ;; :custom - ;; ;; This directory will be used for `obsidian-capture' if set. - ;; (obsidian-inbox-directory "Inbox") - ;; (bind-key (kbd "C-c M-o") 'obsidian-hydra/body 'obsidian-mode-map) - ;; :bind (:map obsidian-mode-map - ;; ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding. - ;; ("C-c C-o" . obsidian-follow-link-at-point) - ;; ;; Jump to backlinks - ;; ("C-c C-b" . obsidian-backlink-jump) - ;; ;; If you prefer you can use `obsidian-insert-link' - ;; ("C-c C-l" . obsidian-insert-wikilink))) - -#+end_src - -*** Anki -:PROPERTIES: -:CUSTOM_ID: h:5854c9a6-1319-4961-a112-75b1bf2e1f69 -:END: - -This section is here to make Anki usable from within Emacs - an endeavour that I have mostly given up on. - -**** Basic Anki setup -:PROPERTIES: -:CUSTOM_ID: h:d20559ed-7ada-4fea-a964-33bfd64b4549 -:END: - -#+begin_src emacs-lisp - - ;; (use-package anki-editor - ;; :after org - ;; :bind (:map org-mode-map - ;; ("" . anki-editor-cloze-region-auto-incr) - ;; ("" . anki-editor-cloze-region-dont-incr) - ;; ("" . anki-editor-reset-cloze-number) - ;; ("" . anki-editor-push-tree)) - ;; :hook (org-capture-after-finalize . anki-editor-reset-cloze-number) ; Reset cloze-number after each capture. - ;; :config - ;; (setq anki-editor-create-decks t ;; Allow anki-editor to create a new deck if it doesn't exist - ;; anki-editor-org-tags-as-anki-tags t) - - ;; (defun anki-editor-cloze-region-auto-incr (&optional arg) - ;; "Cloze region without hint and increase card number." - ;; (interactive) - ;; (anki-editor-cloze-region swarsel-anki-editor-cloze-number "") - ;; (setq swarsel-anki-editor-cloze-number (1+ swarsel-anki-editor-cloze-number)) - ;; (forward-sexp)) - ;; (defun anki-editor-cloze-region-dont-incr (&optional arg) - ;; "Cloze region without hint using the previous card number." - ;; (interactive) - ;; (anki-editor-cloze-region (1- swarsel-anki-editor-cloze-number) "") - ;; (forward-sexp)) - ;; (defun anki-editor-reset-cloze-number (&optional arg) - ;; "Reset cloze number to ARG or 1" - ;; (interactive) - ;; (setq swarsel-anki-editor-cloze-number (or arg 1))) - ;; (defun anki-editor-push-tree () - ;; "Push all notes under a tree." - ;; (interactive) - ;; (anki-editor-push-notes '(4)) - ;; (anki-editor-reset-cloze-number)) - ;; ;; Initialize - ;; (anki-editor-reset-cloze-number) - ;; ) - - ;; (require 'anki-editor) - -#+end_src - -**** Own Anki functions -:PROPERTIES: -:CUSTOM_ID: h:64242e95-6454-4330-bcb9-15353083bade -:END: - -- These functions enable you to quickly set the destination note type and deck - -#+begin_src emacs-lisp - - ;; (defvar swarsel-anki-deck nil) - ;; (defvar swarsel-anki-notetype nil) - ;; (defvar swarsel-anki-fields nil) - - ;; (defun swarsel-anki-set-deck-and-notetype () - ;; (interactive) - ;; (setq swarsel-anki-deck (completing-read "Choose a deck: " - ;; (sort (anki-editor-deck-names) #'string-lessp))) - ;; (setq swarsel-anki-notetype (completing-read "Choose a note type: " - ;; (sort (anki-editor-note-types) #'string-lessp))) - ;; (setq swarsel-anki-fields (progn - ;; (anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,swarsel-anki-notetype))))) - ;; ) - - ;; (defun swarsel-anki-make-template-string () - ;; (if (not swarsel-anki-deck) - ;; (call-interactively 'swarsel-anki-set-deck-and-notetype)) - ;; (setq swarsel-temp swarsel-anki-fields) - ;; (concat (concat "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: " swarsel-anki-notetype "\n:ANKI_DECK: " swarsel-anki-deck "\n:END:\n** ")(pop swarsel-temp) "\n%?\n** " (mapconcat 'identity swarsel-temp "\n\n** ") "\n\n")) - - ;; (defun swarsel-today() - ;; (format-time-string "%Y-%m-%d")) - - ;; (defun swarsel-obsidian-daily () - ;; (interactive) - ;; (if (not (file-exists-p (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))) - ;; (write-region "" nil (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)) - ;; ) - ;; (find-file (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))) - -#+end_src - -** Email + Calendar -:PROPERTIES: -:CUSTOM_ID: h:2f333330-b19d-4f64-85ea-146ff28667e8 -:END: -*** make sure mu4e is found -:PROPERTIES: -:CUSTOM_ID: h:48fde614-7cd0-4764-a7ac-0dae60d8b65a -:END: - -This seems not to be needed - I do not yet dare to delete it though. - -#+begin_src emacs-lisp - - ;; (let ((mu4epath - ;; (concat - ;; (f-dirname - ;; (file-truename - ;; (executable-find "mu"))) - ;; "/../share/emacs/site-lisp/mu4e"))) - ;; (when (and - ;; (string-prefix-p "/nix/store/" mu4epath) - ;; (file-directory-p mu4epath)) - ;; (add-to-list 'load-path mu4epath))) - -#+end_src - -*** mu4e -:PROPERTIES: -:CUSTOM_ID: h:b92a18cf-eec3-4605-a8c2-37133ade3574 -:END: - -In this section we are setting up mu4e, a mail client for emacs using mu with mbsync as backend. The mail accounts themselves are setup in the NixOS configuration, so we only need to add Emacs specific settings here. - -The hook functions are defined here: [[#h:34506761-06b9-43b5-a818-506d9b3faf28][mu4e functions]] - -#+begin_src emacs-lisp - - (use-package mu4e - :ensure nil - ;; :load-path "/usr/share/emacs/site-lisp/mu4e/" - ;;:defer 20 ; Wait until 20 seconds after startup - :config - - ;; This is set to 't' to avoid mail syncing issues when using mbsync - (setq send-mail-function 'sendmail-send-it) - (setq mu4e-change-filenames-when-moving t) - (setq mu4e-mu-binary (executable-find "mu")) - (setq mu4e-hide-index-messages t) - - (setq mu4e-update-interval 180) - (setq mu4e-get-mail-command "mbsync -a") - (setq mu4e-maildir "~/Mail") - - ;; enable inline images - (setq mu4e-view-show-images t) - ;; use imagemagick, if available - (when (fboundp 'imagemagick-register-types) - (imagemagick-register-types)) - - (setq mu4e-drafts-folder "/Drafts") - (setq mu4e-sent-folder "/Sent Mail") - (setq mu4e-refile-folder "/All Mail") - (setq mu4e-trash-folder "/Trash") - - (setq mu4e-maildir-shortcuts - '((:maildir "/leon/Inbox" :key ?1) - (:maildir "/nautilus/Inbox" :key ?2) - (:maildir "/mrswarsel/Inbox" :key ?3) - (:maildir "/Sent Mail" :key ?s) - (:maildir "/Trash" :key ?t) - (:maildir "/Drafts" :key ?d) - (:maildir "/All Mail" :key ?a))) - - (setq user-mail-address "leon@swarsel.win" - user-full-name "Leon Schwarzäugl") - - - (setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com))) - - - (add-hook 'mu4e-compose-mode-hook #'swarsel/mu4e-send-from-correct-address) - (add-hook 'mu4e-compose-post-hook #'swarsel/mu4e-restore-default) -#+end_src - -*** mu4e-alert -:PROPERTIES: -:CUSTOM_ID: h:43209eeb-5d46-472e-b7c2-58a3fb465199 -:END: - -This adds the simple utility of sending desktop notifications whenever a new mail is received. I am using =libnotify= because I want to use this with =notify-send=. - -#+begin_src emacs-lisp - - (use-package mu4e-alert - :config - (setq mu4e-alert-set-default-style 'libnotify)) - - (add-hook 'after-init-hook #'mu4e-alert-enable-notifications) - - (mu4e t) -#+end_src - -*** Calendar -:PROPERTIES: -:CUSTOM_ID: h:c760f04e-622f-4b3e-8916-53ca8cce6edc -:END: - -This provides a beautiful calender to emacs. - -Yes, I am aware that I am exposing my university-calendar to the public here. I can imagine worse things ;) if you however know how to obscure this, let me know! - -#+begin_src emacs-lisp - - (use-package org-caldav - :init - ;; set org-caldav-sync-initalization - (setq swarsel-caldav-synced 0) - (setq org-caldav-url "https://stash.swarsel.win/remote.php/dav/calendars/Swarsele") - (setq org-caldav-calendars - '((:calendar-id "personal" - :inbox "~/Calendars/leon_cal.org"))) - ;; (setq org-caldav-backup-file "~/org-caldav/org-caldav-backup.org") - ;; (setq org-caldav-save-directory "~/org-caldav/") - - :config - (setq org-icalendar-alarm-time 1) - ;; This makes sure to-do items as a category can show up on the calendar - (setq org-icalendar-include-todo t) - ;; This ensures all org "deadlines" show up, and show up as due dates - (setq org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)) - ;; This ensures "scheduled" org items show up, and show up as start times - (setq org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo)) - ) - - (use-package calfw - :ensure nil - :bind ("C-c A" . swarsel/open-calendar) - :init - (use-package calfw-cal - :ensure nil) - (use-package calfw-org - :ensure nil) - (use-package calfw-ical - :ensure nil) - :config - (bind-key "g" 'cfw:refresh-calendar-buffer cfw:calendar-mode-map) - (bind-key "q" 'evil-quit cfw:details-mode-map) - ;; (custom-set-faces - ;; '(cfw:face-title ((t (:foreground "#f0dfaf" :weight bold :height 65)))) - ;; ) - ) - - (defun swarsel/open-calendar () - (interactive) - (unless (eq swarsel-caldav-synced 1) (org-caldav-sync) (setq swarsel-caldav-synced 1)) - ;; (select-frame (make-frame '((name . "calendar")))) ; makes a new frame and selects it - ;; (set-face-attribute 'default (selected-frame) :height 65) ; reduces the font size of the new frame - (cfw:open-calendar-buffer - :contents-sources - (list - (cfw:org-create-source "Purple") ; orgmode source - (cfw:ical-create-source "TISS" "https://tiss.tuwien.ac.at/events/rest/calendar/personal?locale=de&token=4463bf7a-87a3-490a-b54c-99b4a65192f3" "Cyan")))) - -#+end_src - -** Dashboard: emacs startup screen -:PROPERTIES: -:CUSTOM_ID: h:48f5be2b-b3d2-4276-bd49-2630733f23d5 -:END: - -This sets up the =dashboard=, which is really quite useless. But, it looks cool and makes me happy whenever I start an emacsclient without a file name as argument :) - -#+begin_src emacs-lisp - - (use-package dashboard - :ensure t - :config - (dashboard-setup-startup-hook) - ;; (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))) - (setq dashboard-display-icons-p t ;; display icons on both GUI and terminal - dashboard-icon-type 'nerd-icons ;; use `nerd-icons' package - dashboard-set-file-icons t - dashboard-items '((recents . 5) - (projects . 5) - (agenda . 5)) - dashboard-set-footer nil - dashboard-banner-logo-title "Welcome to SwarsEmacs!" - dashboard-image-banner-max-height 300 - dashboard-startup-banner "~/.dotfiles/wallpaper/swarsel.png" - dashboard-projects-backend 'projectile - dashboard-projects-switch-function 'magit-status - dashboard-set-navigator t - dashboard-startupify-list '(dashboard-insert-banner - dashboard-insert-newline - dashboard-insert-banner-title - dashboard-insert-newline - dashboard-insert-navigator - dashboard-insert-newline - dashboard-insert-init-info - dashboard-insert-items - ) - dashboard-navigator-buttons - `(;; line1 - ((,"" - "SwarselSocial" - "Browse Swarsele" - (lambda (&rest _) (browse-url "instagram.com/Swarsele"))) - - (,"" - "SwarselSound" - "Browse SwarselSound" - (lambda (&rest _) (browse-url "sound.swarsel.win")) ) - (,"" - "SwarselSwarsel" - "Browse Swarsel" - (lambda (&rest _) (browse-url "github.com/Swarsel")) ) - (,"" - "SwarselStash" - "Browse SwarselStash" - (lambda (&rest _) (browse-url "stash.swarsel.win")) ) - (,"󰫑" - "SwarselSport" - "Browse SwarselSports" - (lambda (&rest _) (browse-url "social.parkour.wien/@Lenno"))) - ) - ( - (,"󱄅" - "swarsel.win" - "Browse swarsel.win" - (lambda (&rest _) (browse-url "swarsel.win"))) - ) - ))) - - -#+end_src diff --git a/Nix.org b/SwarselSystems.org similarity index 53% rename from Nix.org rename to SwarselSystems.org index 5523e2b..0cc9c12 100644 --- a/Nix.org +++ b/SwarselSystems.org @@ -1,21 +1,121 @@ -#+title: Nix Configuration +#+title: SwarselSystems: NixOS + Emacs Configuration +#+PROPERTY: header-args:emacs-lisp :tangle programs/emacs/init.el :mkdirp yes #+PROPERTY: header-args:nix :mkdirp yes +#+EXPORT_FILE_NAME: index.html +#+macro: revision-date (eval (format-time-string "%F %T %z")) +#+macro: count-words (eval (count-words (point-min) (point-max))) +#+macro: count-lines (eval (count-lines (point-min) (point-max))) + +*This file has {{{count-words}}} words spanning {{{count-lines}}} lines and was last revised on {{{revision-date}}}.* + +In order to have working links and macros when viewing this file online, you might want to switch to the [[https://swarsel.win/swarselsystems/swarselsystems][html version]]. +* Introduction (no code) +:PROPERTIES: +:CUSTOM_ID: h:a86fe971-f169-4052-aacf-15e0f267c6cd +:END: + +This literate configuration file holds the entirety of all configuration files for both NixOS as well as home manager across all machines that I currently use. It also holds an extensive Emacs configuration + +I used to have two separate files (=Emacs.org= and =Nixos.org=) because the NixOS setting for installing Emacs packages used to break if it found UTF-8 characters in =.el= files but not in =.org= files. Hence I used to pass =Emacs.org= to that function rather than =init.el=. This seems to be fixed now however and I was finally able to consolidate both files into one. + +This configuration is part of a NixOS system that is fully declarative and can be found here: + +- [[https:github.com/Swarsel/.dotfiles][~SwarselSystems~ on github.com]] +- [[https:swagit.swarsel.win/Swarsel/.dotfiles][~SwarselSystems~ on swagit.swarsel.win]] + +The literate configuration lets me explain my choices to my future self as well as you, the reader. I go to great lengths to explain the choices for all configuration steps that I take in order for me to pay due diligence in crafting my setup, and not simply copying big chunks of other peoples code. Also, the literate configuration approach is very convenient to me as I only need to keep of (ideally) a single file to manage all of my configuration. I hope that this documentation will make it easier for beginners to get into Emacs and NixOS as I know it can be a struggle in the beginning. + +This file is structured as follows: + +- [[#h:a86fe971-f169-4052-aacf-15e0f267c6cd][Introduction (no code)]] + This is the block you are currently in. It holds no code that actually builds the system, it just outlines the general approach and explains my rough mentality + +- [[#h:d39b8dfb-536d-414f-9fc0-7d67df48cee4][Noweb-Ref blocks]] + This section hold code that can be templated at other parts of the configuration. This is mostly used for the NixOS side of the configuration where I define my host systems that usually have a lot in common. + +- [[#h:02cd20be-1ffa-4904-9d5a-da5a89ba1421][System]] + This section holds all configuration options that apply to NixOS or Home Manager. In other words, here we are doing system and user level configuration. + +- [[#h:ed4cd05c-0879-41c6-bc39-3f1246a96f04][Emacs]] + This section defines my Emacs configuration. For a while, I considered to use rycee's =emacs-init= module ([[https://github.com/nix-community/nur-combined/blob/master/repos/rycee/hm-modules/emacs-init.nix]]) to manage my Emacs configuration; I have since come to the conclusion that this would be a bad idea: at the moment, even though it might seem as I am very bound to the configuration file that you are currently reading, if I ever decide to change how I run my system, I can simply take the generated =.nix= and =.el= files and put them wherever I need them. This file only simplifies that generation without putting further restrictions on my. If I were however to switch to =emacs-init= then I would be indeed to some level confined to the nix ecosystem with my Emacs configuration, as I would no longer have a valid =.org= file to manage it with, instead generating an =init.el= directly from nix code. I like to keep that level of freedom for potential future use. Also, you will notice there is no package system setup in this configuration. This is because packages are automatically handled on the NixOS side by parsing the generated =init.el= file for package installs. + + My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: {{{revision-date}}}) + +#+begin_src emacs-lisp :tangle no :exports both + + system-configuration-options + +#+end_src + +#+RESULTS: +: --prefix=/nix/store/lymgpfqr5dp1wc0khbcbhhjnxq8ccsy9-emacs-pgtk-20240521.0 --disable-build-details --with-modules --with-pgtk --with-compress-install --with-toolkit-scroll-bars --with-native-compilation --without-imagemagick --with-mailutils --without-small-ja-dic --with-tree-sitter --without-xinput2 --with-xwidgets --with-dbus --with-selinux + + This file is not loaded by Emacs directly as the configuration (even though this would be possible) - instead, it generates two more files: + +- =early-init.el= + This file handle startup optimization and sets up the basic frame that I will be working in. + +- =init.el= + This file handles the rest of the Emacs configuration. + +By using the configuration offered by this file, the file you are reading right now (=SwarselSystems.org=) will be freshly tangled on every file save. However, when you clone this configuration yourself and have not yet activated it, you need to tangle the file yourself. This can be done using the keybind =C-c C-v t=. Alternatively, execute the following block: + + #+begin_src emacs-lisp :tangle no :export both :results silent + + (org-babel-tangle) + + #+end_src + +Lastly, I add this javascript bit to the file in order to have a darkmode toggle when exporting to html: + +#+begin_src elisp :noweb yes :exports both :results html + (concat + "\n" + "") +#+end_src + +#+RESULTS: +#+begin_export html + + +#+end_export -This file holds the entirety of all configuration files for both NixOS as well as home manager across all machines that I currently use. -The only exception is my Emacs configuration which is held in its own .org file - this is done because that allows for upon-switch parsing of that file and installation of all emacs-packages through home-manager. The init.el file cannot be used there as it contains UTF-8 characters that would break the nix function responsible for handling the emacs-package building process. * Noweb-Ref blocks +:PROPERTIES: +:CUSTOM_ID: h:d39b8dfb-536d-414f-9fc0-7d67df48cee4 +:END: + +These blocks are used in several places throughout the configurations, but not on all machines necessarily. For example, the theming section needs to be in a NixOS block on NixOS machines but in a home-manager block on non-NixOS. This serves to reduce code duplication. -These blocks are used in several places throughout the configurations, but not on all machines necessarily. For example, the themeing section needs to be in a NixOS block on NixOS machines but in a home-manager block on non-NixOS. This serves to reduce code duplication. +** Non-NixOS +:PROPERTIES: +:CUSTOM_ID: h:237b9f25-1fa3-484e-952e-99175dbb91c5 +:END: -** All systems -*** Themeing +These blocks are to be used on systems that are not running NixOS. For example, one such system would be a Fedora system running home manager, where the respective NixOS features might not be available. -This is where the theme for the whole OS is defined. This noweb-ref section cannot be copied to the general NixOS config for now since they are on different levels in the config, which would make the flake impure. +*** Theme +:PROPERTIES: +:CUSTOM_ID: h:5bc1b0c9-dc59-4c81-b5b5-e60699deda78 +:END: -#+begin_src nix :noweb-ref theme +This is where the theme for the whole OS is defined. This noweb-ref section cannot be copied to the general NixOS config for now since they are on different folder structure levels in the config, which would make the flake impure. +For styling, I am using the [[https://github.com/danth/stylix][stylix]] NixOS module, loaded by flake. This package is really great, as it adds nix expressions for basically everything. Ever since switching to this, I did not have to play around with theming anywhere else. + +#+begin_src nix :tangle no :noweb-ref theme stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; @@ -66,30 +166,44 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** Waybar items - LAPTOPS - -#+begin_src nix :noweb-ref waybarlaptop - - programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark" - "mpris" - "custom/left-arrow-light" - "network" - "custom/left-arrow-dark" - "pulseaudio" - "custom/left-arrow-light" - "custom/pseudobat" - "battery" - "custom/left-arrow-dark" - "group/hardware" - "custom/left-arrow-light" - "clock#2" - "custom/left-arrow-dark" - "clock#1" - ]; +:PROPERTIES: +:CUSTOM_ID: h:f3cf9bdc-6826-4d8e-ba5a-253ef098a9b8 +:END: + +This noweb-ref block defines some aspects of my waybar configuration. Mainly, it adds the =battery= module to the waybar, which is no needed on PCs. + +The most part of this configuration is done here: [[#h:0bf51f63-01c0-4053-a591-7f0c5697c690][Waybar]] + +#+begin_src nix :tangle no :noweb-ref waybarlaptop + + programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark" + "mpris" + "custom/left-arrow-light" + "network" + "custom/left-arrow-dark" + "pulseaudio" + "custom/left-arrow-light" + "custom/pseudobat" + "battery" + "custom/left-arrow-dark" + "group/hardware" + "custom/left-arrow-light" + "clock#2" + "custom/left-arrow-dark" + "clock#1" + ]; #+end_src *** Waybar items - PC +:PROPERTIES: +:CUSTOM_ID: h:47749e76-3f25-485a-9e98-c7ce3a4ad444 +:END: -#+begin_src nix :noweb-ref waybarpc +As stated above, this is the waybar configuration for PCs now. Here we do not need the battery module. However, this leads to a slight problem with theming: my waybar modules alternate their background-color between black and grey. The battery module is usually on grey background. If I were to simply delete that, I would now have two modules on black background. To avoid this, I define a pseudo-module =custom/pseudobat= that simply shows a static image and calls =wlogout= on right click. This wastes a little bit of screen space, but that is a price I am willing to pay for consistency. + +The most part of this configuration is done here: [[#h:0bf51f63-01c0-4053-a591-7f0c5697c690][Waybar]] + +#+begin_src nix :tangle no :noweb-ref waybarpc programs.waybar.settings.mainBar."custom/pseudobat"= { format= ""; @@ -113,26 +227,44 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann ]; #+end_src -** NixOS *** Sway Startup commands +:PROPERTIES: +:CUSTOM_ID: h:515cfeb6-3b16-4fb2-9222-3557555a6cc1 +:END: + +This defines programs I want to have starting when I start the system -#+begin_src nix :noweb-ref startupnixos +Part of the startup is also defined in [[#h:02df9dfc-d1af-4a37-a7a0-d8da0af96a20][Sway]]. The distinction is as follows. As this configuration also needs to work on systems that are running only home manager, I probably need to run nixGL or something similar on those systems to get these graphic apps to display properly. In this section we only define such graphical programs, in the other location we only put shell applications and such. + +These other apps currently include: +- spotifytui +- kitty + +Do not that =syncthingtray= is also not mentioned here. It is installed as a home manager package that automatically starts at system start. + +#+begin_src nix :tangle no :noweb-ref startupnixos { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } #+end_src *** gpg-agent +:PROPERTIES: +:CUSTOM_ID: h:66fd578f-d4a0-4e17-bf3d-a9eb64bc7103 +:END: + +This section holds most of the configuration needed for the gpg-agent. This allows me to use my Yubikey during normal system operation as well in Emacs (with some extra configuration here: [[#h:d2c7323d-f8c6-4f23-b70a-930e3e4ecce5][Magit]]) -#+begin_src nix :noweb-ref gpgagent +Also, there are some more NixOS related options here: [[#h:7a89b5e3-b700-4167-8b14-2b8172f33936][Yubikey settings]] + +I also enable the extra socket here for ssh agent forwarding. But I have not fully gotten it to work yet. + +#+begin_src nix :tangle no :noweb-ref gpgagent services.gpg-agent = { enable = true; @@ -147,9 +279,23 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src +** NixOS +:PROPERTIES: +:CUSTOM_ID: h:996e9c5f-ed65-4f4f-b043-5a901ed74358 +:END: + +These settings are to be used only on full NixOS setups. + *** Wrap with hardware-configuration +:PROPERTIES: +:CUSTOM_ID: h:4ae8b4ed-47a8-4d79-a12c-894118ea57e1 +:END: + +This handles the automactically generated =/etc/nixos/hardware-configuration.nix= file that sets some hardware specific settings automatically upon creating the NixOS system. -#+begin_src nix :noweb-ref wrap +This sections used to handle more imports, but at the moment, it is now pretty useless really. + +#+begin_src nix :tangle no :noweb-ref wrap imports = [ @@ -159,8 +305,13 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** Virtual hosts init +:PROPERTIES: +:CUSTOM_ID: h:a4585ec3-8fa0-472c-a0db-1b34917591ea +:END: + +This sections is for common NixoS settings that I use for my NixoS LXC images that I run on Proxmox. Proxmox requires special attention to run along with NixOS in any capacity. -#+begin_src nix :noweb-ref vminitbare +#+begin_src nix :tangle no :noweb-ref vminitbare services.xserver = { @@ -192,7 +343,9 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src -#+begin_src nix :noweb yes :noweb-ref vminit +This is again the =hardware-configuration.nix= wrap that you saw earlier, however for Proxmox systems we need to add some more NixOS modules for compatibility. + +#+begin_src nix :tangle no :noweb yes :noweb-ref vminit imports = [ (modulesPath + "/virtualisation/proxmox-lxc.nix") @@ -204,9 +357,26 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src ** flake.nix +:PROPERTIES: +:CUSTOM_ID: h:c7588c0d-2528-485d-b2df-04d6336428d7 +:END: + +Handling the flake.nix file used to be a bit of a chore, since it felt like writing so much boilerplate code just to define new systems. The noweb-approach here makes this a little bit less painful. + +These blocks are later inserted here: [[#h:aee5ec75-7ca6-40d8-b6ac-a3e7e33a474b][flake.nix template]]. Adding new flake inputs is very easy, you just add them to [[#h:8a411ee2-a58e-4b5b-99bd-4ba772f8f0a2][Inputs & Inputs@Outputs]] first by name in the first source-block, and then the path in the second source-block. Any variables to be set for the host configuration are done in [[#h:df0072bc-853f-438f-bd85-bfc869501015][let]], and the specific setup is done in either [[#h:9c9b9e3b-8771-44fa-ba9e-5056ae809655][nixosConfigurations]] (for NixOS systems), [[#h:f881aa05-a670-48dd-a57b-2916abdcb692][homeConfigurations]] (for home-manager systems), or [[#h:5f6ef553-59f9-4239-b6f3-63d33b57f335][nixOnDroidConfigurations]] (for Nix on Android). There is also the [[#h:6a08495a-8566-4bb5-9fac-b03df01f6c81][nixos-generators]] section that currently just defines a Proxmox LXC image. + *** Inputs & Inputs@Outputs +:PROPERTIES: +:CUSTOM_ID: h:8a411ee2-a58e-4b5b-99bd-4ba772f8f0a2 +:END: -#+begin_src nix :noweb-ref flakeinputsatoutputs +Here we define inputs and outputs of the flake. First, the following list is for the outputs of the flake. + +Format: , + +Mind the comma at the end. You need this because the =...= is being passed as the last argument in the template at [[#h:aee5ec75-7ca6-40d8-b6ac-a3e7e33a474b][flake.nix template]]. + +#+begin_src nix :tangle no :noweb-ref flakeinputsatoutputs nixpkgs, home-manager, @@ -218,7 +388,6 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann stylix, sops-nix, lanzaboote, - pia, nixpkgs-mautrix-signal, nix-gaming, nixos-hardware, @@ -226,7 +395,36 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src -#+begin_src nix :noweb-ref flakeinputs +Here, just add the input names, urls and other options that are needed, like =nixpkgs.follows=. By using the latter option, you tell the package to not provide it's own package repository, but instead 'nest' itself into another, which is very useful. +A short overview over each input and what it does: + +- nixkpkgs + This is the base repository that I am following for all packages. I follow the unstable branch. +- [[https://github.com/nix-community/home-manager][home-manager]] + This handles user-level configuration and mostly provides dotfiles that are generated and symlinked to =~/.config/=. +- [[https://github.com/nix-community/NUR][NUR]] + The nix user repository contains user provided modules, packages and expressions. These are not audited by the nix community, so be aware of supply chain vulnerabilities when using those. I am only really using rycee's firefox addons from there which saves me a lot of hassle, and it seems to be a safe resource. +- [[https://github.com/nix-community/nixGL][nixGL]] + This solves the problem that nix has with "OpenGL", as libraries are not linked and programs will often fail to find drivers. But I do not fully understand what it does. All I know is that I usually have to use this on non-NIxoS systems. +- [[https://github.com/danth/stylix][stylix]] + As described before, this handles all theme related options. +- [[https://github.com/Mic92/sops-nix][sops-nix]] + This provides declarative secrets management for NixOS and home manager using sops and age keys. It is a bit more cumbersome to use on home manager systems - which is a bother because I then have to resort to that configuration to keep everything supported - but it is super practical and really the primary reason why it makes sense for me to go for NixOS, as I do not have to do any extra secrets provisioning. +- [[https://github.com/nix-community/lanzaboote][Lanzaboote]] + Provides secure boot for NixOS. Needed for my Surface Pro 3. +- [[https://github.com/nix-community/nix-on-droid][nix-on-droid]] + This brings nix to android in an app that is similar to tmux! Of course most of the configuration does not apply to this, but it is still neat to have! +- [[https://github.com/nix-community/nixos-generators][nixos-generators]] + Provides me with images that I can use to create LXCs on Proxmox. +- [[https://github.com/fufexan/nix-gaming][nix-gaming]] + Provides some gaming related tweaks for NixOS +- [[https://github.com/NixOS/nixos-hardware][nixos-hardware]] + Provides specific hardware setting for some hardware configurations. For example, this sets some better defaults for my Lenovo Thinkpad P14s Gen2. +- [[https://github.com/thiagokokada/nix-alien][nix-alien]] + This is supposed to allow me to run unpatched libraries directly without a need for ELF patching or resorting to =steam-run=. However, I have not yet gotten this to work. + + +#+begin_src nix :tangle no :noweb-ref flakeinputs nixpkgs.url = github:nixos/nixpkgs/nixos-unstable; @@ -251,7 +449,7 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann # provides GL to non-NixOS hosts nixgl.url = github:guibou/nixGL; - # manages all themeing using Home-Manager + # manages all theming using Home-Manager stylix.url = github:danth/stylix; # nix secrets management @@ -272,12 +470,6 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann inputs.nixpkgs.follows = "nixpkgs"; }; - # privateinternetaccess nixos integration - not sure if I will keep using - pia = { - url = "git+https://git.sr.ht/~rprospero/nixos-pia?ref=development"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - # provides expressions for mautrix-signal nixpkgs-mautrix-signal ={ url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal; @@ -300,8 +492,17 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** let +:PROPERTIES: +:CUSTOM_ID: h:df0072bc-853f-438f-bd85-bfc869501015 +:END: + +Here I define a few variables that I need for my system specifications. First and foremost, =pkgs=, which gets passed the emacs-overlay, nur, and nixgl modules to it. With this, I can grab all these packages by referencing =pkgs.= instead of having to put e.g. =nixgl.auto.nixGLDefault=. + +I also define =armpkgs=, which are simply built for ARM hosts, i.e. my virtual machines that I have in the Oracle Cloud. -#+begin_src nix :noweb-ref flakelet +Lastly I define some common module lists that I can simply load depending on the fundamental system (NixOS vs. non-NixOS). + +#+begin_src nix :tangle no :noweb-ref flakelet system = "x86_64-linux"; # not very portable, but I do not use other architectures at the moment pkgs = import nixpkgs { inherit system; @@ -348,8 +549,13 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** nixosConfigurations +:PROPERTIES: +:CUSTOM_ID: h:9c9b9e3b-8771-44fa-ba9e-5056ae809655 +:END: + +This section is the biggest pain point of the configuration. For every system, I have one of these. I know there are better ways to go about this, but I did not find the time yet to look into this further. For now, enjoy this meter-long list -#+begin_src nix :noweb-ref flakenixosconf +#+begin_src nix :tangle no :noweb-ref flakenixosconf onett = nixpkgs.lib.nixosSystem { specialArgs = {inherit inputs pkgs; }; @@ -455,7 +661,6 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann specialArgs = {inherit inputs pkgs; }; modules = [ sops-nix.nixosModules.sops - pia.nixosModule ./profiles/server1/transmission/nixos.nix ]; }; @@ -519,8 +724,13 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann }; #+end_src *** homeConfigurations +:PROPERTIES: +:CUSTOM_ID: h:f881aa05-a670-48dd-a57b-2916abdcb692 +:END: -#+begin_src nix :noweb-ref flakehomeconf +In contrast, this defines home-manager systems, which I only have one of. + +#+begin_src nix :tangle no :noweb-ref flakehomeconf "leons@PCisLee" = home-manager.lib.homeManagerConfiguration { inherit pkgs; @@ -532,8 +742,13 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** nixOnDroidConfigurations +:PROPERTIES: +:CUSTOM_ID: h:5f6ef553-59f9-4239-b6f3-63d33b57f335 +:END: + +Nix on Android also demands an own flake output, which is provided here. -#+begin_src nix :noweb-ref flakedroidconf +#+begin_src nix :tangle no :noweb-ref flakedroidconf default = nix-on-droid.lib.nixOnDroidConfiguration { modules = [ @@ -544,8 +759,23 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src *** nixos-generators +:PROPERTIES: +:CUSTOM_ID: h:6a08495a-8566-4bb5-9fac-b03df01f6c81 +:END: -#+begin_src nix :noweb-ref flakenixosgenerators +This builds my proxmox template. It is defined as a separate output so that I can already apply some rudimentary configuration before even setting up the system. + +Usage: + +#+begin_src shell :tangle no :export both + +nix build ~/.dotfiles/#proxmox-lxc + +#+end_src + +The resulting image can then be loaded in Proxmox. + +#+begin_src nix :nowe:tangle no b-ref flakenixosgenerators proxmox-lxc = nixos-generators.nixosGenerate { inherit system; @@ -557,13 +787,24 @@ This is where the theme for the whole OS is defined. This noweb-ref section cann #+end_src -* TODO System specific configuration +* System +:PROPERTIES: +:CUSTOM_ID: h:02cd20be-1ffa-4904-9d5a-da5a89ba1421 +:END: + +** System specific configuration +:PROPERTIES: +:CUSTOM_ID: h:88bf4b90-e94b-46fb-aaf1-a381a512860d +:END: This section mainly exists house different `configuration.nix` files for system level configurations of NixOS systems as well as `home.nix` for user level configurations on all systems. -Important: Think about if a settings really needs to go into this area - chances are that the settings can also go to the general settings. +Important: Think about if a settings really needs to go into this area - chances are that the settings can also go to the general settings, which is to be preferred in order to reduce code duplication. -** Template (for new machines) +*** Template (for new machines) +:PROPERTIES: +:CUSTOM_ID: h:29a3066d-4da6-4f61-b835-5e4a43e2d34d +:END: This section holds the minimum configuration that is needed on a new host. These assume a NixOS machine (so not standalone home-manager on a non-NixOS host), as this is the setting that I will most likely use in the future now. All of these blocks need to be updated, with entries called TEMPLATE mostly needed to be filled with host-/user-specific values or other inputs. If TEMPLATE is given in a comment section, see the provided values as likely defaults. The TEMPLATE comments should afterwards be deleted for clarity. @@ -571,7 +812,10 @@ If a non-NixOS host must be used, check the Surface configuration for pointers. No matter what you do, check the initial /etc/nixos/configuration.nix for notable changes that might emerge in future versions of nix. -*** NixOS +**** NixOS +:PROPERTIES: +:CUSTOM_ID: h:91c428e5-f56e-4d36-b08f-7819b2979b23 +:END: #+begin_src nix :noweb yes :tangle profiles/TEMPLATE/nixos.nix @@ -627,7 +871,10 @@ No matter what you do, check the initial /etc/nixos/configuration.nix for notabl #+end_src -*** Home Manager +**** Home Manager +:PROPERTIES: +:CUSTOM_ID: h:a08e51ee-88eb-4241-917d-68b4bdbcf171 +:END: #+begin_src nix :noweb yes :tangle profiles/TEMPLATE/home.nix { config, pkgs, lib, fetchFromGitHub, ... }: @@ -715,27 +962,41 @@ No matter what you do, check the initial /etc/nixos/configuration.nix for notabl #+end_src -** Physical hosts -*** TODO Surface +*** Physical hosts +:PROPERTIES: +:CUSTOM_ID: h:58dc6384-0d19-4f71-9043-4014bd033ba2 +:END: +**** Surface +:PROPERTIES: +:CUSTOM_ID: h:42339b42-c64b-4d0c-a80c-5c44d3423fce +:END: -My Surface Pro 3, only used for on-the-go university work. Be careful when pushing largechanges to this machine, as it easily runs out of memory on large switches. At the moment the only machine running non-NixOS, so special care must be taken not to break this one during updates. +My Surface Pro 3, only used for on-the-go university work. Be careful when pushing large changes to this machine, as it easily runs out of memory on large switches. At the moment the only machine running non-NixOS, so special care must be taken not to break this one during updates. -**** TODO Channel setup +***** Channel setup +:PROPERTIES: +:CUSTOM_ID: h:42e45181-9d78-4266-a9a0-9621032f38b0 +:END: This installs nixGL, which is needed to run GL apps installed through home-manager, since this machine is not using NixOS. -- TODO: move this to flake.nix by using an overlay +This is not super clean (because it is not fully replicative), but I do not really care. 1) Install nixGL: -#+begin_src nix +#+begin_src nix :tangle no nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update nix-env -iA nixgl.auto.nixGLDefault # or replace `nixGLDefault` with your desired wrapper #+end_src This is needed in order to use EGL. Prefix programs that use it with `nixGL` -**** Home manager +***** Home manager +:PROPERTIES: +:CUSTOM_ID: h:929d56f5-e16f-4341-901c-24e8a8450398 +:END: + +Special things to note here: We are running xcape to allow =CAPS= to act as =CTRL= and =ESC=. Also we are using =nixGL= in most places. #+begin_src nix :noweb yes :tangle profiles/surface/home.nix @@ -871,11 +1132,17 @@ This is needed in order to use EGL. Prefix programs that use it with `nixGL` #+end_src -*** Onett (Lenovo Y510P) +**** Onett (Lenovo Y510P) +:PROPERTIES: +:CUSTOM_ID: h:6bc7b9c7-ccfd-42d7-982a-97907aa28b80 +:END: My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately. -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:20fc100c-045d-468a-9bf2-824037e6785b +:END: #+begin_src nix :noweb yes :tangle profiles/onett/nixos.nix @@ -937,7 +1204,10 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar #+end_src -**** Home Manager +***** Home Manager +:PROPERTIES: +:CUSTOM_ID: h:d35847ae-2207-4417-9858-b0ea7e2b1a0b +:END: #+begin_src nix :noweb yes :tangle profiles/onett/home.nix @@ -1039,157 +1309,17 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar #+end_src -*** Twoson (Lenovo Thinkpad T14s Gen3) -**** NixOS - -#+begin_src nix :noweb yes :tangle profiles/twoson/nixos.nix - - { config, lib, pkgs, inputs, ... }: - - { - - <> - - services = { - getty.autologinUser = "swarsel"; - greetd.settings.initial_session.user="swarsel"; - }; - - # Bootloader - # boot.loader.grub.enable = true; - # boot.loader.grub.device = "/dev/sda"; # TEMPLATE - if only one disk, this will work - # boot.loader.grub.useOSProber = true; - - # -------------------------------------- - # you might need a configuration like this instead: - # Bootloader - # boot.loader.grub.enable = true; - # boot.loader.grub.devices = ["nodev" ]; - # boot.loader.grub.useOSProber = true; - # boot.kernelPackages = pkgs.linuxPackages_latest; - # -------------------------------------- - - networking.hostName = "twoson"; # Define your hostname. - - stylix.image = ../../wallpaper/t14swp.png; - <> - - # Configure keymap in X11 (only used for login) - services.xserver = { - layout = "us"; - xkbVariant = "altgr-intl"; - }; - - users.users.swarsel = { - isNormalUser = true; - description = "TEMPLATE"; - extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ]; - packages = with pkgs; []; - }; - - environment.systemPackages = with pkgs; [ - ]; - - system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change - - - } - -#+end_src - -**** Home Manager -#+begin_src nix :noweb yes :tangle profiles/twoson/home.nix - - { config, pkgs, lib, fetchFromGitHub, ... }: - - { - - <> - home = { - username = "swarsel"; - homeDirectory = "/home/swarsel"; - stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing. - keyboard.layout = "us"; # TEMPLATE - home.packages = with pkgs; [ - # --------------------------------------------------------------- - # if schildichat works on this machine, use it, otherwise go for element - # element-desktop - # --------------------------------------------------------------- - ]; - }; - # update path if the sops private key is stored somewhere else - sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ]; - - # waybar config - TEMPLATE - update for cores and temp - programs.waybar.settings.mainBar = { - #cpu.format = "{icon0} {icon1} {icon2} {icon3}"; - cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}"; - temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input"; - }; - - # ----------------------------------------------------------------- - # is this machine always connected to power? If yes, use this block: - # <> - # ----------------------------------------------------------------- - - # ----------------------------------------------------------------- - # if not always connected to power (laptop), use this (default): - <> - # ----------------------------------------------------------------- - - wayland.windowManager.sway= { - config = rec { - # update for actual inputs here, - input = { - "36125:53060:splitkb.com_Kyria_rev3" = { - xkb_layout = "us"; - xkb_variant = "altgr-intl"; - }; - # "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE - # xkb_layout = "us"; - # xkb_options = "grp:win_space_toggle"; - # # xkb_options = "ctrl:nocaps,grp:win_space_toggle"; - # xkb_variant = "altgr-intl"; - # }; - "type:touchpad" = { - dwt = "enabled"; - tap = "enabled"; - natural_scroll = "enabled"; - middle_emulation = "enabled"; - }; - - }; - - output = { - DP-1 = { - mode = "1920x1280"; # TEMPLATE - scale = "1"; - bg = "~/.dotfiles/wallpaper/t14swp.png fill"; - }; - }; - - keybindings = let - modifier = config.wayland.windowManager.sway.config.modifier; - in { - # TEMPLATE - "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\""; - # "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\""; - }; - - startup = [ - <> - ]; - }; - }; - } - -#+end_src - -*** Threed (Surface Pro 3) +**** Threed (Surface Pro 3) +:PROPERTIES: +:CUSTOM_ID: h:7b1a8f91-ef43-433c-ba4c-c5baf50e1de4 +:END: New setup for the SP3, this time using NixOS - another machine will take over the HM-only config for compatibility in the future. -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:980f1aca-28b3-4ed7-ae7f-6d8cdc28dea1 +:END: #+begin_src nix :noweb yes :tangle profiles/threed/nixos.nix @@ -1243,7 +1373,10 @@ New setup for the SP3, this time using NixOS - another machine will take over th #+end_src -**** Home Manager +***** Home Manager +:PROPERTIES: +:CUSTOM_ID: h:449c20d8-338a-483c-a6f0-9a164a6071d6 +:END: #+begin_src nix :noweb yes :tangle profiles/threed/home.nix { config, pkgs, lib, fetchFromGitHub, ... }: @@ -1323,8 +1456,19 @@ New setup for the SP3, this time using NixOS - another machine will take over th } #+end_src -*** Fourside (Lenovo Thinkpad P14s Gen2) -**** NixOS +**** Fourside (Lenovo Thinkpad P14s Gen2) +:PROPERTIES: +:CUSTOM_ID: h:6c6e9261-dfa1-42d8-ab2a-8b7c227be6d9 +:END: + +My new main machine. + +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:ab6fefc4-aabd-456c-8a21-5fcb20c02869 +:END: + +Mostly just sets some opened ports for several games, enables virtualbox (which I do not want everywhere because of resource considerations) and enables thinkfan, which allows for better fan control on Lenovo Thinkpad machines. #+begin_src nix :noweb yes :tangle profiles/fourside/nixos.nix @@ -1433,7 +1577,13 @@ New setup for the SP3, this time using NixOS - another machine will take over th #+end_src -**** Home Manager +***** Home Manager +:PROPERTIES: +:CUSTOM_ID: h:85f7110c-2f25-4506-b64a-fce29f29d0d0 +:END: + +This is basically just adjusted to the core count, path to the =hwmon= (this was very bothersome on this machine due to changing address), as well as making use of the top-row function keys. + #+begin_src nix :noweb yes :tangle profiles/fourside/home.nix { config, pkgs, lib, fetchFromGitHub, ... }: @@ -1518,177 +1668,74 @@ New setup for the SP3, this time using NixOS - another machine will take over th #+end_src -*** Stand +*** Virtual hosts +:PROPERTIES: +:CUSTOM_ID: h:4dc59747-9598-4029-aa7d-92bf186d6c06 +:END: -My home PC, the most powerful machine. Sadly Sway cannot make good use out of it's NVIDIA card, so it runs a dual boot setup with a kind of broken GRUB that does not autodetect the windows partition. +My server setup is built on Proxmox VE; back when I started, I created all kinds of wild Debian/Ubuntu/etc. KVMs and LXCs on there. However, the root disk has suffered a weird failure where it has become unable to be cloned, but it is still functional for now. I am currently rewriting all machines on there to use NixOS instead; this is a ongoing process. -**** NixOS +In the long run, I am thinking about a transition to kubernetes or using just a server running NixOS and using the built-in container functionality. For now however, I like the network management provided by Proxmox, as I am a bit intimidated by doing that from scratch. -#+begin_src nix :noweb yes :tangle profiles/stand/nixos.nix +**** TEMPLATE +:PROPERTIES: +:CUSTOM_ID: h:292c583e-0b67-4456-bdba-a72d4e53ce66 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:598a2a4c-4d99-46d6-9d4a-dd9e73704f09 +:END: - { config, lib, pkgs, inputs, ... }: +#+begin_src nix :tangle profiles/server1/TEMPLATE/nixos.nix + + { pkgs, modulesPath, ... }: { + imports = [ + (modulesPath + "/virtualisation/proxmox-lxc.nix") + ]; - <> + environment.systemPackages = with pkgs; [ + git + gnupg + ssh-to-age + ]; - services = { - getty.autologinUser = "homelen"; - greetd.settings.initial_session.user="homelen"; + services.xserver = { + layout = "us"; + xkbVariant = "altgr-intl"; }; - stylix.image = ../../wallpaper/standwp.png; - <> + nix.settings.experimental-features = ["nix-command" "flakes"]; - # Bootloader. - boot.loader.grub = { + proxmoxLXC.manageNetwork = true; # manage network myself + proxmoxLXC.manageHostName = false; # manage hostname myself + networking.hostName = "TEMPLATE"; # Define your hostname. + networking.useDHCP = true; + networking.enableIPv6 = false; + networking.firewall.enable = false; + services.openssh = { enable = true; - devices = ["nodev" ]; - useOSProber = true; + settings.PermitRootLogin = "yes"; }; - - # boot.kernelPackages = pkgs.linuxPackages_latest; - networking = { - hostName = "stand"; # Define your hostname. - enableIPv6 = false; - firewall.enable = false; - # networkmanager.enable = true; - }; - - hardware = { - bluetooth.enable = true; - }; - - users.users.homelen = { - isNormalUser = true; - description = "Leon S"; - extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ]; - packages = with pkgs; []; - }; - - environment.systemPackages = with pkgs; [ - ]; - - system.stateVersion = "23.05"; # Did you read the comment? Dont change this basically - - } - -#+end_src - -**** Home Manager -#+begin_src nix :noweb yes :tangle profiles/stand/home.nix - - { config, pkgs, lib, fetchFromGitHub, ... }: - - { - - <> - - home = { - username = "homelen"; - homeDirectory = "/home/homelen"; - stateVersion = "23.05"; # Please read the comment before changing. - keyboard.layout = "us"; - packages = with pkgs; [ - ]; - }; - - sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ]; - - services.blueman-applet.enable = true; - - # waybar config - programs.waybar.settings.mainBar = { - cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}"; - temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input"; - }; - <> - - wayland.windowManager.sway= { - config = rec { - input = { - "36125:53060:splitkb.com_Kyria_rev3" = { - xkb_layout = "us"; - xkb_variant = "altgr-intl"; - }; - }; - - output = { - DP-1 = { - mode = "2560x1440"; - scale = "1"; - bg = "~/.dotfiles/wallpaper/standwp.png fill"; - }; - }; - - keybindings = let - modifier = config.wayland.windowManager.sway.config.modifier; - in { - "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\""; - }; - - startup = [ - <> - ]; - }; - }; - } - -#+end_src - -** Virtual hosts - -My server setup is built on Proxmox VE; back when I started, I created all kinds of wild Debian/Ubuntu/etc. KVMs and LXCs on there. However, the root disk has suffered a weird failure where it has become unable to be cloned, but it is still functional for now. I am currently rewriting all machines on there to use NixOS instead; this is a ongoing process. - -In the long run, I am thinking about a transition to kubernetes or using just a server running NixOS and using the built-in container functionality. For now however, I like the network management provided by Proxmox, as I am a bit intimidated by doing that from scratch. - -*** TEMPLATE -**** NixOS - -#+begin_src nix :tangle profiles/server1/TEMPLATE/nixos.nix - - { pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/virtualisation/proxmox-lxc.nix") - ]; - - environment.systemPackages = with pkgs; [ - git - gnupg - ssh-to-age - ]; - - services.xserver = { - layout = "us"; - xkbVariant = "altgr-intl"; - }; - - nix.settings.experimental-features = ["nix-command" "flakes"]; - - proxmoxLXC.manageNetwork = true; # manage network myself - proxmoxLXC.manageHostName = false; # manage hostname myself - networking.hostName = "TEMPLATE"; # Define your hostname. - networking.useDHCP = true; - networking.enableIPv6 = false; - networking.firewall.enable = false; - services.openssh = { - enable = true; - settings.PermitRootLogin = "yes"; - }; - users.users.root.openssh.authorizedKeys.keyFiles = [ - ../../../secrets/keys/authorized_keys - ]; - # users.users.root.password = "TEMPLATE"; + users.users.root.openssh.authorizedKeys.keyFiles = [ + ../../../secrets/keys/authorized_keys + ]; + # users.users.root.password = "TEMPLATE"; system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change } #+end_src -*** NGINX -**** NixOS +**** NGINX +:PROPERTIES: +:CUSTOM_ID: h:90340ea4-5ef0-4466-92cf-12d8ece805ba +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:519899ad-adcd-435b-8857-71635afbc756 +:END: #+begin_src nix :tangle profiles/server1/nginx/nixos.nix @@ -1897,7 +1944,10 @@ In the long run, I am thinking about a transition to kubernetes or using just a #+end_src -*** [Manual steps required] Calibre +**** [Manual steps required] Calibre +:PROPERTIES: +:CUSTOM_ID: h:12152533-a000-4e7e-8038-43f8e501cedd +:END: This machine requires manual setup: 1) (obsolete for now) Set up calibre-web: @@ -1915,7 +1965,10 @@ This machine requires manual setup: In general, I am not amazed by this setup; Kavita is the reader of choice, calibre-web mostly is there to have a convenient way to fullfill the opinionated folder structure when uploading ebooks (calibre-web does not work on its own since it forces sqlite which does not work nicely with my NFS book store). I hope that in the future Kavita will implement ebook upload, or that calibre-web will ditch the sqlite constraints. -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:0094ccd0-36e4-46cb-a422-6f1aefb786d6 +:END: #+begin_src nix :tangle profiles/server1/calibre/nixos.nix @@ -2023,8 +2076,14 @@ In general, I am not amazed by this setup; Kavita is the reader of choice, calib #+end_src -*** Jellyfin -**** NixOS +**** Jellyfin +:PROPERTIES: +:CUSTOM_ID: h:4a194546-9a9e-47c4-8d03-8d2428d45d30 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:9e94efd9-f63b-46ce-b34c-ec3128de5ed9 +:END: #+begin_src nix :tangle profiles/server1/jellyfin/nixos.nix @@ -2108,11 +2167,17 @@ In general, I am not amazed by this setup; Kavita is the reader of choice, calib #+end_src -*** [WIP/Incomplete/Untested] Transmission +**** [WIP/Incomplete/Untested] Transmission +:PROPERTIES: +:CUSTOM_ID: h:dffc1243-8d6a-4cac-8a5d-3a27d4546235 +:END: This stuff just does not work, I seem to be unable to create a working VPN Split Tunneling on NixOS. Maybe this is introduced by the wonky Proxmox-NixOS container interaction, I am not sure. For now, this machine does not work at all and I am stuck with my Debian Container that does this for me ... -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:2a2ebf94-b262-4e83-ab86-d8b1ebec492d +:END: #+begin_src nix :tangle profiles/server1/transmission/nixos.nix @@ -2477,7 +2542,10 @@ This stuff just does not work, I seem to be unable to create a working VPN Split #+end_src -*** [Manual steps needed] Matrix +**** [Manual steps needed] Matrix +:PROPERTIES: +:CUSTOM_ID: h:1d6221c4-1f48-4f83-b262-5298ed99218e +:END: 1) After the initial setup, run the - /run/secrets-generated/matrix_user_register.sh @@ -2488,7 +2556,10 @@ command to register a new admin user. Make sure to also do this for doublepuppet.yaml 3) Restart postgresql.service, matrix-synapse.service, mautrix-whatsapp.service, mautrix-telegram.service -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:a0b2d610-7258-4875-adb4-9ec4afe05b02 +:END: #+begin_src nix :noweb yes :tangle profiles/server1/matrix/nixos.nix @@ -2763,8 +2834,14 @@ Make sure to also do this for doublepuppet.yaml #+end_src -*** Sound -**** NixOS +**** Sound +:PROPERTIES: +:CUSTOM_ID: h:b36415bf-77fa-4d51-842c-8cde0e46b844 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:4bb55d69-9e09-4338-9f1e-a77ce37f02ed +:END: #+begin_src nix :noweb yes :tangle profiles/server1/sound/nixos.nix @@ -2867,8 +2944,14 @@ Make sure to also do this for doublepuppet.yaml } #+end_src -*** Spotifyd -**** NixOS +**** Spotifyd +:PROPERTIES: +:CUSTOM_ID: h:23032961-346c-4141-97b9-a4d5469dc7d8 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:857bb1f6-9aeb-4600-ac79-a85ef011c847 +:END: #+begin_src nix :noweb yes :tangle profiles/server1/spotifyd/nixos.nix @@ -2925,8 +3008,14 @@ Make sure to also do this for doublepuppet.yaml #+end_src -*** Sync -**** NixOS +**** Sync +:PROPERTIES: +:CUSTOM_ID: h:4c5febb0-fdf6-44c5-8d51-7ea0f8930abf +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:e5fbb73a-799a-438f-a88c-fc14d110ac9c +:END: #+begin_src nix :tangle profiles/remote/oracle/sync/nixos.nix @@ -3093,12 +3182,18 @@ Make sure to also do this for doublepuppet.yaml #+end_src -*** [Manual steps required] Swatrix -**** NixOS +**** [Manual steps required] Swatrix +:PROPERTIES: +:CUSTOM_ID: h:39553a9c-7095-4db8-b0df-bf47d91cb937 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:441d367d-cddd-40d7-9db7-d170e61e1c52 +:END: The files mentioned by -#+begin_src nix +#+begin_src nix :tangle no settings.app_service_config_files = [ "/var/lib/matrix-synapse/telegram-registration.yaml" @@ -3111,7 +3206,7 @@ The files mentioned by need to be moved to the corresponding location. The below files are created as soon as the appservice is run once. This means that matrix will crash on the first startup; afterwards run these commands and restart the service. -#+begin_src shell +#+begin_src shell :tangle no cp /var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse/ chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/telegram-registration.yaml @@ -3124,7 +3219,7 @@ need to be moved to the corresponding location. The below files are created as s as for the contents of doublepuppet.yaml: -#+begin_src yaml doublepuppet.yaml +#+begin_src yaml :tangle no id: doublepuppet url: as_token: doublepuppet @@ -3485,8 +3580,14 @@ Lastly, the machine that runs matrix needs to regularly update, as otherwise you #+end_src -*** Paperless -**** NixOS +**** Paperless +:PROPERTIES: +:CUSTOM_ID: h:17b9ba9d-94c9-44d5-99dd-776174d4bcc9 +:END: +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:1fc355ca-ca8c-4b02-ab3f-5656f2992112 +:END: #+begin_src nix :noweb yes :tangle profiles/server1/paperless/nixos.nix @@ -3538,11 +3639,17 @@ Lastly, the machine that runs matrix needs to regularly update, as otherwise you #+end_src -*** Sandbox (Lenovo Y510P) +**** Sandbox (Lenovo Y510P) +:PROPERTIES: +:CUSTOM_ID: h:60cf171f-2ec9-418f-8f67-85d159efe9d0 +:END: My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately. -**** NixOS +***** NixOS +:PROPERTIES: +:CUSTOM_ID: h:23b0f629-343c-42fa-bf9b-70bea341c0d2 +:END: #+begin_src nix :noweb yes :tangle profiles/sandbox/nixos.nix @@ -4415,13 +4522,24 @@ My laptop, sadly soon to be replaced by a new one, since most basic functions ar } #+end_src -* Common NixOS +** Common NixOS +:PROPERTIES: +:CUSTOM_ID: h:1c1250cd-e9b4-4715-8d9f-eb09e64bfc7f +:END: These are system-level settings specific to NixOS machines. All settings that are required on all machines go here. -** General +*** General +:PROPERTIES: +:CUSTOM_ID: h:5a114da6-ef8d-404d-b31b-b51472908e77 +:END: + +**** Enable home-manager module +:PROPERTIES: +:CUSTOM_ID: h:45e4315b-0929-4c47-b65a-c8f0a685f4df +:END: -Section for all settings that are not really deserving of their own section. +First, we enable the use of =home-manager= as a NixoS module #+begin_src nix :tangle profiles/common/nixos.nix @@ -4431,26 +4549,46 @@ Section for all settings that are not really deserving of their own section. home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; +#+end_src + +**** Setup login keymap +:PROPERTIES: +:CUSTOM_ID: h:7248f338-8cad-4443-9060-deae7955b26f +:END: - # login keymap +Next, we setup the keymap in case we are not in a graphical session. At this point, I always resort to us/altgr-intl, as it is extremly comfortable to use + +#+begin_src nix :tangle profiles/common/nixos.nix services.xserver = { xkb.layout = "us"; xkb.variant = "altgr-intl"; }; - # mount NAS drive - # works only at home, but w/e - # fileSystems."/mnt/smb" = { - # device = "//192.168.1.3/Eternor"; - # fsType = "cifs"; - # options = let - # # this line prevents hanging on network split - # automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s"; - # in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"]; - # }; +#+end_src + +**** Enable flakes and nix-command +:PROPERTIES: +:CUSTOM_ID: h:f9718641-adf7-4e5b-9e07-5b9413224971 +:END: + +Next, we need to make sure that flakes stay enabled when we rebuild the configuration. At the same time we enable the experimental =nix-command=, which enables commands such as the more powerful =nix shell= as opposed to =nix-shell=. + +#+begin_src nix :tangle profiles/common/nixos.nix - # # enable flakes - urgent line!! nix.settings.experimental-features = ["nix-command" "flakes"]; + +#+end_src + +**** Environment setup +:PROPERTIES: +:CUSTOM_ID: h:f4006367-0965-4b4f-a3b0-45f63b07d2b8 +:END: + +Next, we will setup some environment variables that need to be set on the system-side. We apply some compatibility options for chromium apps on wayland, enable the wordlist and make metadata reading possible for my file explorer (nautilus). + +#+begin_src nix :tangle profiles/common/nixos.nix + + # use ozone for wayland - chromium apps environment.sessionVariables.NIXOS_OZONE_WL = "1"; # wordlist for look @@ -4463,32 +4601,58 @@ Section for all settings that are not really deserving of their own section. gst-libav ]); - # correct time between linux and windows +#+end_src + +**** Make sure time is consistent in windows dualboot +:PROPERTIES: +:CUSTOM_ID: h:c31f7900-f8b7-46aa-b501-c245ab889578 +:END: + +Windows/Linux dualboot has the quirk of ruining the system clock. Fix it on this side. + +#+begin_src nix :tangle profiles/common/nixos.nix + time.hardwareClockInLocalTime = true; +#+end_src + +**** Disallow stylix from styling grub +:PROPERTIES: +:CUSTOM_ID: h:b09c2bb6-86b7-46f4-a855-ac21dd9988b2 +:END: + +By default, [[https://github.com/danth/stylix][stylix]] wants to style GRUB as well. However, I think that looks horrible. + +#+begin_src nix :tangle profiles/common/nixos.nix + # dont style GRUB with stylix stylix.targets.grub.enable = false; # the styling makes grub more ugly - # cura fix - # xdg.portal = { - # enable = true; - # extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; - # wlr.enable = true; - # config = { - # common = { - # default = [ - # "*" - # ]; - # }; - # }; - # }; - # wayland-related +#+end_src + +**** Enable PolicyKit +:PROPERTIES: +:CUSTOM_ID: h:e2d40df9-0026-4caa-8476-9dc2353055a1 +:END: + +Needed for control over system-wide privileges etc. + +#+begin_src nix :tangle profiles/common/nixos.nix + security.polkit.enable = true; - hardware.opengl = { - enable = true; - driSupport = true; - driSupport32Bit = true; - }; + +#+end_src + + +**** Reduce systemd timeouts +:PROPERTIES: +:CUSTOM_ID: h:12858442-c129-4aa1-9c9c-a0916e36b302 +:END: + +There is a persistent bug over Linux kernels that makes the user wait 1m30s on system shutdown due to the reason =a stop job is running for session 1 of user ...=. I do not want to wait that long and am confident no important data is lost by doing this. + +#+begin_src nix :tangle profiles/common/nixos.nix + # systemd systemd.extraConfig = '' @@ -4496,96 +4660,138 @@ Section for all settings that are not really deserving of their own section. DefaultTimeoutStopSec=15s ''; - # audio - sound.enable = true; - # nixpkgs.config.pulseaudio = true; - hardware.pulseaudio= { - enable = true; - package = pkgs.pulseaudioFull; - }; - hardware.enableAllFirmware = true; - hardware.bluetooth.powerOnBoot = true; - hardware.bluetooth.settings = { - General = { - Enable = "Source,Sink,Media,Socket"; +#+end_src + +**** Hardware settings +:PROPERTIES: +:CUSTOM_ID: h:1fa7cf61-5c03-43a3-a7f0-3d6ee246b31b +:END: + +Enable OpenGL, Sound, Bluetooth and various drivers. + + #+begin_src nix :tangle profiles/common/nixos.nix + + hardware.opengl = { + enable = true; + driSupport = true; + driSupport32Bit = true; }; - }; - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - networking.networkmanager.enable = true; - time.timeZone = "Europe/Vienna"; + sound.enable = true; + hardware.pulseaudio= { + enable = true; + package = pkgs.pulseaudioFull; + }; - i18n.defaultLocale = "en_US.UTF-8"; - i18n.extraLocaleSettings = { - LC_ADDRESS = "de_AT.UTF-8"; - LC_IDENTIFICATION = "de_AT.UTF-8"; - LC_MEASUREMENT = "de_AT.UTF-8"; - LC_MONETARY = "de_AT.UTF-8"; - LC_NAME = "de_AT.UTF-8"; - LC_NUMERIC = "de_AT.UTF-8"; - LC_PAPER = "de_AT.UTF-8"; - LC_TELEPHONE = "de_AT.UTF-8"; - LC_TIME = "de_AT.UTF-8"; - }; + hardware.enableAllFirmware = true; + + hardware.bluetooth.powerOnBoot = true; + hardware.bluetooth.settings = { + General = { + Enable = "Source,Sink,Media,Socket"; + }; + }; #+end_src -** System Packages +**** Common network settings +:PROPERTIES: +:CUSTOM_ID: h:7d696b64-debe-4a95-80b5-1e510156a6c6 +:END: -Mostly used to install some compilers and lps's that I want to have available when not using a devShell flake. +Here I only enable =networkmanager=. Most of the 'real' network config is done in [[#h:88bf4b90-e94b-46fb-aaf1-a381a512860d][System specific configuration]]. #+begin_src nix :tangle profiles/common/nixos.nix - environment.systemPackages = with pkgs; [ - # yubikey packages - gnupg - yubikey-personalization - yubikey-personalization-gui - yubico-pam - # yubioath-flutter - # yubikey-manager - # yubikey-manager-qt - yubico-piv-tool - # pinentry - - # theme related - gnome.adwaita-icon-theme - - # kde-connect - xdg-desktop-portal - - # bluetooth - bluez - - # lsp-related ------------------------------- - # nix - # latex - texlab - ghostscript_headless - # wireguard - wireguard-tools - # rust - rust-analyzer - clippy - rustfmt - # cpp - clang-tools - # + cuda - cudatoolkit - #lsp-bridge / python - gcc - gdb - (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) - # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) - # -------------------------------------------- - - (stdenv.mkDerivation { + networking.networkmanager.enable = true; + +#+end_src + +**** Locale settings +:PROPERTIES: +:CUSTOM_ID: h:852d59ab-63c3-4831-993d-b5e23b877796 +:END: + +Setup timezone and locale. I want to use the US layout, but have the rest adapted to my country and timezone. + +#+begin_src nix :tangle profiles/common/nixos.nix + + time.timeZone = "Europe/Vienna"; + + i18n.defaultLocale = "en_US.UTF-8"; + i18n.extraLocaleSettings = { + LC_ADDRESS = "de_AT.UTF-8"; + LC_IDENTIFICATION = "de_AT.UTF-8"; + LC_MEASUREMENT = "de_AT.UTF-8"; + LC_MONETARY = "de_AT.UTF-8"; + LC_NAME = "de_AT.UTF-8"; + LC_NUMERIC = "de_AT.UTF-8"; + LC_PAPER = "de_AT.UTF-8"; + LC_TELEPHONE = "de_AT.UTF-8"; + LC_TIME = "de_AT.UTF-8"; + }; + +#+end_src + +*** System Packages +:PROPERTIES: +:CUSTOM_ID: h:0e7e8bea-ec58-499c-9731-09dddfc39532 +:END: + +Mostly used to install some compilers and lsp's that I want to have available when not using a devShell flake. Most other packages should go in [[#h:893a7f33-7715-415b-a895-2687ded31c18][Installed packages]]. + +#+begin_src nix :tangle profiles/common/nixos.nix + + environment.systemPackages = with pkgs; [ + # yubikey packages + gnupg + yubikey-personalization + yubikey-personalization-gui + yubico-pam + # yubioath-flutter + # yubikey-manager + # yubikey-manager-qt + yubico-piv-tool + # pinentry + + # theme related + gnome.adwaita-icon-theme + + # kde-connect + xdg-desktop-portal + + # bluetooth + bluez + + # lsp-related ------------------------------- + # nix + # latex + texlab + ghostscript_headless + # wireguard + wireguard-tools + # rust + rust-analyzer + clippy + rustfmt + # cpp + clang-tools + # + cuda + cudatoolkit + #lsp-bridge / python + gcc + gdb + (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) + # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) + # -------------------------------------------- + + (stdenv.mkDerivation { name = "oama"; src = pkgs.fetchurl { - name = "oama"; - url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz"; - sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc="; + name = "oama"; + url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz"; + sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc="; }; phases = [ @@ -4598,16 +4804,16 @@ Mostly used to install some compilers and lps's that I want to have available wh mv $out/oama-0.13.1-Linux-x86_64-static/oama $out/bin/ ''; - }) - - - + }) - ]; + ]; #+end_src -** Programs (including zsh setup) +*** Programs (including zsh setup) +:PROPERTIES: +:CUSTOM_ID: h:2bbf5f31-246d-4738-925f-eca40681f7b6 +:END: Some programs profit from being installed through dedicated NixOS settings on system-level; these go here. Notably the zsh setup goes here and cannot be deleted under any circumstances. @@ -4627,20 +4833,53 @@ Some programs profit from being installed through dedicated NixOS settings on sy #+end_src -** Services +*** Services +:PROPERTIES: +:CUSTOM_ID: h:79f3258f-ed9d-434d-b50a-e58d57ade2a7 +:END: Setting up some hardware services as well as keyboard related settings. Here we make sure that we can use the CAPS key as a ESC/CTRL double key, which is a lifesaver. +**** blueman +:PROPERTIES: +:CUSTOM_ID: h:b91df05b-113d-4d09-93d1-b271e5b76810 +:END: + +Enables the blueman service including the nice system tray icon. + #+begin_src nix :tangle profiles/common/nixos.nix services.blueman.enable = true; +#+end_src + +**** Detect Scanners over network +:PROPERTIES: +:CUSTOM_ID: h:fae5939e-22ac-4532-a10e-0b86013d20ce +:END: + +This allows me to use my big scanner/printer's scanning function over the network. + +#+begin_src nix :tangle profiles/common/nixos.nix + # enable scanners over network hardware.sane = { enable = true; extraBackends = [ pkgs.sane-airscan ]; }; +#+end_src + +**** Detect Printers over network +:PROPERTIES: +:CUSTOM_ID: h:8c13df62-c6d9-4a0a-83be-d77e71628f0b +:END: + +This allows me to use my big scanner/printer's printing function over the network. Avahi is the service used for the network discovery, the rest of the settings are driver related. + + +#+begin_src nix :tangle profiles/common/nixos.nix + # enable discovery and usage of network devices (esp. printers) services.printing.enable = true; services.printing.drivers = [ @@ -4661,60 +4900,91 @@ Setting up some hardware services as well as keyboard related settings. Here we openFirewall = true; }; - # nautilus file manager - services.gvfs.enable = true; - - # Make CAPS work as a dual function ESC/CTRL key - services.interception-tools = { - enable = true; - udevmonConfig = let - dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" '' - TIMING: - TAP_MILLISEC: 200 - DOUBLE_TAP_MILLISEC: 0 - - MAPPINGS: - - KEY: KEY_CAPSLOCK - TAP: KEY_ESC - HOLD: KEY_LEFTCTRL - ''; - in '' - - JOB: | - ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \ - | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \ - | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE - DEVICE: - EVENTS: - EV_KEY: [KEY_CAPSLOCK] - ''; - }; - #+end_src -** Yubikey settings - -It makes sense to house these settings in their own section, since they are all needed really. Note that the starting of the gpg-agent is done in the sway settings, to also perform this step of the setup for non NixOS-machines at the same time. +**** enable GVfs +:PROPERTIES: +:CUSTOM_ID: h:f101daa2-604d-4553-99e2-f64b9c207f51 +:END: -The exception is the system packages, since that cannot be defined twice in the same file (common.nix). The comment is left in as a remider for that. +This is being set to allow myself to use all functions of nautilus in NixOS #+begin_src nix :tangle profiles/common/nixos.nix - programs.ssh.startAgent = false; + services.gvfs.enable = true; - services.pcscd.enable = true; +#+end_src - # environment.systemPackages = with pkgs; [ - # --- IN SYSTEM PACKAGES SECTION --- - # ]; +**** interception-tools: Make CAPS work as ESC/CTRL +:PROPERTIES: +:CUSTOM_ID: h:08d213d5-a9f4-4309-8635-ba557b01dc7d +:END: - services.udev.packages = with pkgs; [ - yubikey-personalization - ]; +This is a super-convenient package that lets my remap my =CAPS= key to =ESC= if pressed shortly, and =CTRL= if being held. + +#+begin_src nix :tangle profiles/common/nixos.nix + + # Make CAPS work as a dual function ESC/CTRL key + services.interception-tools = { + enable = true; + udevmonConfig = let + dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" '' + TIMING: + TAP_MILLISEC: 200 + DOUBLE_TAP_MILLISEC: 0 + + MAPPINGS: + - KEY: KEY_CAPSLOCK + TAP: KEY_ESC + HOLD: KEY_LEFTCTRL + ''; + in '' + - JOB: | + ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \ + | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \ + | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE + DEVICE: + EVENTS: + EV_KEY: [KEY_CAPSLOCK] + ''; + }; + +#+end_src + +*** Yubikey settings +:PROPERTIES: +:CUSTOM_ID: h:7a89b5e3-b700-4167-8b14-2b8172f33936 +:END: + +It makes sense to house these settings in their own section, since they are all needed really. Note that the starting of the gpg-agent is done in the sway settings, to also perform this step of the setup for non NixOS-machines at the same time. + +=pcscd= is needed to use the smartcard mode (CCID) of the Yubikey. + +The exception is the system packages, since that cannot be defined twice in the same file (common.nix). The comment is left in as a remider for that. + +The rest of the gpg-agent related settings are here: [[#h:66fd578f-d4a0-4e17-bf3d-a9eb64bc7103][gpg-agent]] + +#+begin_src nix :tangle profiles/common/nixos.nix + + programs.ssh.startAgent = false; + + services.pcscd.enable = true; + + # environment.systemPackages = with pkgs; [ + # --- IN SYSTEM PACKAGES SECTION --- + # ]; + + services.udev.packages = with pkgs; [ + yubikey-personalization + ]; #+end_src -** System Login +*** System Login +:PROPERTIES: +:CUSTOM_ID: h:eae45839-223a-4027-bce3-e26e092c9096 +:END: This section houses the greetd related settings. I do not really want to use a display manager, but it is useful to have setup in some ways - in my case for starting sway on system startup. Notably the default user login setting that is commented out here goes into the *system specific* settings, make sure to update it there @@ -4741,13 +5011,26 @@ This section houses the greetd related settings. I do not really want to use a d #+end_src -* Common Home-Manager -** Installed packages +** Common Home-Manager +:PROPERTIES: +:CUSTOM_ID: h:f0a6b5e0-2157-4522-b5e1-3f0abd91c05e +:END: +*** Installed packages +:PROPERTIES: +:CUSTOM_ID: h:893a7f33-7715-415b-a895-2687ded31c18 +:END: Here are defined some packages that I would like to use across all my machines. Most of these should not require further setup. Notably the cura package is severely outdated on nixpkgs, so I just fetch a more recent AppImage and run that instead. Also, I define some useful shell scripts here. +Programming languages and default lsp's are defined here: [[#h:0e7e8bea-ec58-499c-9731-09dddfc39532][System Packages]] + +**** Packaged +:PROPERTIES: +:CUSTOM_ID: h:6ef9bb5f-c5ee-496e-86e2-d8d271a34d75 +:END: + #+begin_src nix :tangle profiles/common/home.nix { config, pkgs, lib, fetchFromGitHub , ... }: @@ -4905,6 +5188,15 @@ Also, I define some useful shell scripts here. noto-fonts noto-fonts-cjk-sans +#+end_src + +**** Self-defined +:PROPERTIES: +:CUSTOM_ID: h:96cbea91-ff13-4120-b8a9-496b2fa96e70 +:END: + +#+begin_src nix :tangle profiles/common/home.nix + # cura (let cura5 = appimageTools.wrapType2 rec { name = "cura5"; @@ -5043,17 +5335,6 @@ Also, I define some useful shell scripts here. ''; }) - # (pkgs.writeShellApplication { - # name = "cdr"; - # runtimeInputs = [ pkgs.fzf ]; - # text = '' - # cdr () { - # cd "$( (find /home/swarsel/Documents/GitHub -maxdepth 1 && echo /home/swarsel/.dotfiles) | fzf )" - # } - # cdr - # ''; - # }) - (pkgs.writeShellApplication { name = "bak"; text = '' @@ -5063,13 +5344,12 @@ Also, I define some useful shell scripts here. ]; - - # MIGHT NEED TO ENABLE THIS ON SURFACE!! - - #+end_src -** sops +*** sops +:PROPERTIES: +:CUSTOM_ID: h:d87d80fd-2ac7-4f29-b338-0518d06b4deb +:END: I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine: - `ssh-keygen -t ed25519 -C "NAME sops"` in .ssh directory (or wherever) - name e.g. "sops" @@ -5083,23 +5363,18 @@ I use sops-nix to handle secrets that I want to have available on my machines at sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; sops.validateSopsFiles = false; - # sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; - # This will generate a new key if the key specified above does not exist - # sops.age.generateKey = true; - - # sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; # since we are using the home-manager implementation, we need to specify the runtime path for each secret sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; sops.secrets.swarselmail = {path = "/run/user/1000/secrets/swarselmail";}; sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; - # sops.secrets.leon = { }; - # sops.secrets.nautilus = { }; - # sops.secrets.mrswarsel = { }; #+end_src -** SSH Machines +*** SSH Machines +:PROPERTIES: +:CUSTOM_ID: h:edd6720e-1f90-40bf-b6f9-30a19d4cae08 +:END: It is very convenient to have SSH aliases in place for machines that I use. This is mainly used for some server machines and some university clusters. @@ -5238,44 +5513,25 @@ It is very convenient to have SSH aliases in place for machines that I use. This #+end_src -** Fonts + Theme +*** Fonts + Theme +:PROPERTIES: +:CUSTOM_ID: h:a92318cd-413e-4e78-a478-e63b09df019c +:END: These section allows home-manager to allow theme settings, and handles some other appearance-related settings like cursor styles. Interestingly, system icons (adwaita) still need to be setup on system-level, and will break if defined here. +This section has been notably empty ever since switching to stylix. Only Emacs is not allowed to be styled by it, because it becomes more ugly compared to my handcrafted setup. + #+begin_src nix :tangle profiles/common/home.nix stylix.targets.emacs.enable = false; - # fonts.fontconfig.enable = true; - # gtk = { - # enable = true; - - # theme = { - # name = "Arc-Dark"; - # package = pkgs.arc-theme; - # }; - - # cursorTheme = { - # name = "capitaine-cursors"; - # package = pkgs.capitaine-cursors; - # }; - - # gtk3.extraConfig = { - # Settings = '' - # gtk-application-prefer-dark-theme=1 - # ''; - # }; - - # gtk4.extraConfig = { - # Settings = '' - # gtk-application-prefer-dark-theme=1 - # ''; - # }; - # }; - #+end_src -** TODO Desktop Entries +*** Desktop Entries +:PROPERTIES: +:CUSTOM_ID: h:867556e6-5a24-4c43-9d47-3edca2f16488 +:END: Some programs lack a dmenu launcher - I define them myself here. @@ -5309,8 +5565,6 @@ TODO: Non-NixOS machines (=sp3) should not use these by default, but instead the # categories = [ "Application"]; # }; - # currently unused but kept for possible future use-case - # not needed as long as schildichat is working properly element = { name = "Element Matrix Client"; genericName = "Element"; @@ -5323,12 +5577,13 @@ TODO: Non-NixOS machines (=sp3) should not use these by default, but instead the #+end_src -** TODO Sourcing dotfiles and environment variables +*** Linking dotfiles +:PROPERTIES: +:CUSTOM_ID: h:5ef03803-e150-41bc-b603-e80d60d96efc +:END: This section should be used in order to symlink already existing configuration files using `home.file` and setting session variables using `home.sessionVariables`. -TODO At the moment this is only used for emacs' init.el and early-init.el - I plan to migrate them to a NUR emacs-init setup at some point in the future, but that is a lot of work and will take possibly more time than I am able to spend on this right now - As for the `home.sessionVariables`, it should be noted that environment variables that are needed at system start should NOT be loaded here, but instead in `programs.zsh.config.extraSessionCommands` (in the home-manager programs section). This is also where all the wayland related variables are stored. #+begin_src nix :tangle profiles/common/home.nix @@ -5353,23 +5608,37 @@ As for the `home.sessionVariables`, it should be noted that environment variable }; }; +#+end_src + +*** Sourcing environment variables +:PROPERTIES: +:CUSTOM_ID: h:4486b02f-4fb8-432b-bfa2-2e786206341d +:END: + +#+begin_src nix :tangle profiles/common/home.nix + home.sessionVariables = { EDITOR = "bash ~/.dotfiles/scripts/editor.sh"; - EDITORBAK = "bash ~/.dotfiles/scripts/editor.sh"; - # GTK_THEME = "Arc-Dark"; }; #+end_src -** Programs -*** General +*** Programs +:PROPERTIES: +:CUSTOM_ID: h:070a75ce-e209-4cda-aa25-e979bbf75d47 +:END: + +This houses the configurations for all programs managed by home-manager. + +**** General Programs: bottom, imv, sioyek, bat, carapace, wlogout, swayr, yt-dlp, mpv, jq, nix-index, ripgrep, pandoc, fzf +:PROPERTIES: +:CUSTOM_ID: h:f0e0b580-2e1c-4ca6-a983-f05d3ebbbcde +:END: + +This section is for programs that require no further configuration #+begin_src nix :tangle profiles/common/home.nix - programs.password-store = { - enable = true; - package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); - }; # zsh Integration is enabled by default for these programs.bottom.enable = true; programs.imv.enable = true; @@ -5385,11 +5654,51 @@ As for the `home.sessionVariables`, it should be noted that environment variable programs.ripgrep.enable = true; programs.pandoc.enable = true; programs.fzf.enable = true; + programs.zoxide.enable = true; + +#+end_src + +*** password-store +:PROPERTIES: +:CUSTOM_ID: h:ac0e5e62-0dbf-4782-9a96-9e558eae86ae +:END: + +Enables password store with the =pass-otp= extension which allows me to store and generate one-time-passwords. + +#+begin_src nix :tangle profiles/common/home.nix + + programs.password-store = { + enable = true; + package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); + }; + +#+end_src + +*** direnv +:PROPERTIES: +:CUSTOM_ID: h:1ab84307-b3fb-4c32-9def-4b89a53a8547 +:END: + +Enables direnv, which I use for nearly all of my nix dev flakes. + +#+begin_src nix :tangle profiles/common/home.nix + programs.direnv = { enable = true; nix-direnv.enable = true; }; - programs.zoxide.enable = true; + +#+end_src + +*** eza +:PROPERTIES: +:CUSTOM_ID: h:1bd6b0c7-f201-43e2-9624-6c50de00a1f6 +:END: + +Eza provides me with a better =ls= command and some other useful aliases. + +#+begin_src nix :tangle profiles/common/home.nix + programs.eza = { enable = true; icons = true; @@ -5399,6 +5708,18 @@ As for the `home.sessionVariables`, it should be noted that environment variable "--group-directories-first" ]; }; + + #+end_src + +*** git +:PROPERTIES: +:CUSTOM_ID: h:419675ec-3310-438e-80ae-9eaa798a319d +:END: + +Here I set up my git config, automatic signing of commits, useful aliases for my ost used commands (for when I am not using [[#h:d2c7323d-f8c6-4f23-b70a-930e3e4ecce5][Magit]]) as well as a git template defined in [[#h:5ef03803-e150-41bc-b603-e80d60d96efc][Linking dotfiles]]. + + #+begin_src nix :tangle profiles/common/home.nix + programs.git = { enable = true; aliases = { @@ -5439,6 +5760,11 @@ As for the `home.sessionVariables`, it should be noted that environment variable #+end_src *** Fuzzel +:PROPERTIES: +:CUSTOM_ID: h:069cabf3-df14-49ba-8d17-75f2bcf34fbf +:END: + +Here I only need to set basic layout options - the rest is being managed by stylix. #+begin_src nix :tangle profiles/common/home.nix @@ -5447,19 +5773,9 @@ As for the `home.sessionVariables`, it should be noted that environment variable settings = { main = { layer = "overlay"; - # font = "Monospace:size=8"; lines = "10"; width = "40"; }; - colors = { - # background="293744dd"; - # text="f8f8f2ff"; - # match="8be9fdff"; - # selection-match="8be9fdff"; - # selection="44475add"; - # selection-text="f8f8f2ff"; - # border="ffd700ff"; - }; border.radius = "0"; }; }; @@ -5467,6 +5783,11 @@ As for the `home.sessionVariables`, it should be noted that environment variable #+end_src *** Starship +:PROPERTIES: +:CUSTOM_ID: h:55212502-c8f6-43af-ae99-55c8377ef34e +:END: + +Starship makes my =zsh= look cooler! I have symbols for most programming languages and toolchains, also I build my own powerline. #+begin_src nix :tangle profiles/common/home.nix @@ -5577,9 +5898,14 @@ As for the `home.sessionVariables`, it should be noted that environment variable #+end_src *** Kitty +:PROPERTIES: +:CUSTOM_ID: h:5f1287db-d2e8-49aa-8c58-730129c7795c +:END: Kitty is the terminal emulator of choice for me, it is nice to configure using nix, fast, and has a nice style. +The theme is handled by stylix. + #+begin_src nix :tangle profiles/common/home.nix programs.kitty = { @@ -5590,15 +5916,18 @@ Kitty is the terminal emulator of choice for me, it is nice to configure using n "ctrl+shift+home" = "no_op"; "ctrl+shift+end" = "no_op"; }; - # theme = "citylights"; }; #+end_src *** zsh +:PROPERTIES: +:CUSTOM_ID: h:91dd4cc4-aada-4e74-be23-0cc69ed85af5 +:END: -zsh is clearly the most convenient shell for me and it happens to be super neat to configure within home manager. +zsh is the most convenient shell for me and it happens to be super neat to configure within home manager. +Here we set some aliases (some of them should be shellApplications instead) as well as some zsh plugins like =fzf-tab=. #+begin_src nix :tangle profiles/common/home.nix @@ -5654,6 +5983,11 @@ zsh is clearly the most convenient shell for me and it happens to be super neat #+end_src *** Mail +:PROPERTIES: +:CUSTOM_ID: h:506d01fc-c20b-473a-ac78-bce4b53fe0e3 +:END: + +Normally I use 4 mail accounts - here I set them all up. Three of them are Google accounts (sadly), which are a chore to setup. The last is just a sender account that I setup SMTP for here. #+begin_src nix :tangle profiles/common/home.nix @@ -5787,62 +6121,76 @@ zsh is clearly the most convenient shell for me and it happens to be super neat #+end_src -*** Emacs +*** Home-manager: Emacs +:PROPERTIES: +:CUSTOM_ID: h:c05d1b64-7110-4151-b436-46bc447113b4 +:END: -#+begin_src nix :tangle profiles/common/home.nix +By using the emacs-overlay NixOS module, I can install all Emacs packages that I want to use right through NixOS. This is done by passing my =init.el= file to the configuration which will then be parsed upon system rebuild, looking for =use-package= sections in the Elisp code. Also I define here the style of Emacs that I want to run - I am going with native Wayland Emacs here (=emacs-pgtk=). All of the nice options such as =tree-sitter= support are enabled by default, so I do not need to adjust the build process. - # enable emacs overlay for bleeding edge features - # also read init.el file and install use-package packages - programs.emacs = { - enable = true; - package = (pkgs.emacsWithPackagesFromUsePackage { - config = ../../Emacs.org; # tangling my Emacs.org file here instead of directly putting init.el allows avoidance of automatically installing packages in blocks using UTF-8 characters, which would break the nix evaluation happening in this line. This line is also the reason why (for now) the Emacs configuration lives in a different .org file - package = pkgs.emacs-pgtk; - alwaysEnsure = true; - alwaysTangle = true; - extraEmacsPackages = epkgs: [ - epkgs.mu4e - epkgs.use-package - epkgs.lsp-bridge - epkgs.doom-themes - - # build the rest of the packages myself - # org-calfw is severely outdated on MELPA and throws many warnings on emacs startup - # build the package from the haji-ali fork, which is well-maintained - (epkgs.trivialBuild rec { - pname = "calfw"; - version = "1.0.0-20231002"; - src = pkgs.fetchFromGitHub { - owner = "haji-ali"; - repo = "emacs-calfw"; - rev = "bc99afee611690f85f0cd0bd33300f3385ddd3d3"; - hash = "sha256-0xMII1KJhTBgQ57tXJks0ZFYMXIanrOl9XyqVmu7a7Y="; - }; - packageRequires = [ epkgs.howm ]; - }) +Lastly, I am defining some more packages here that the parser has problems finding. Also there are some packages that are not in ELPA or MELPA that I still want to use, like =calfw= and =fast-scroll=, so I build them here. - (epkgs.trivialBuild rec { - pname = "fast-scroll"; - version = "1.0.0-20191016"; - src = pkgs.fetchFromGitHub { - owner = "ahungry"; - repo = "fast-scroll"; - rev = "3f6ca0d5556fe9795b74714304564f2295dcfa24"; - hash = "sha256-w1wmJW7YwXyjvXJOWdN2+k+QmhXr4IflES/c2bCX3CI="; - }; - packageRequires = []; - }) +#+begin_src nix :tangle profiles/common/home.nix - ]; - }); - }; + # enable emacs overlay for bleeding edge features + # also read init.el file and install use-package packages + programs.emacs = { + enable = true; + package = (pkgs.emacsWithPackagesFromUsePackage { + config = ../../programs/emacs/init.el; + package = pkgs.emacs-pgtk; + alwaysEnsure = true; + alwaysTangle = true; + extraEmacsPackages = epkgs: [ + epkgs.mu4e + epkgs.use-package + epkgs.lsp-bridge + epkgs.doom-themes + + # build the rest of the packages myself + # org-calfw is severely outdated on MELPA and throws many warnings on emacs startup + # build the package from the haji-ali fork, which is well-maintained + (epkgs.trivialBuild rec { + pname = "calfw"; + version = "1.0.0-20231002"; + src = pkgs.fetchFromGitHub { + owner = "haji-ali"; + repo = "emacs-calfw"; + rev = "bc99afee611690f85f0cd0bd33300f3385ddd3d3"; + hash = "sha256-0xMII1KJhTBgQ57tXJks0ZFYMXIanrOl9XyqVmu7a7Y="; + }; + packageRequires = [ epkgs.howm ]; + }) + + (epkgs.trivialBuild rec { + pname = "fast-scroll"; + version = "1.0.0-20191016"; + src = pkgs.fetchFromGitHub { + owner = "ahungry"; + repo = "fast-scroll"; + rev = "3f6ca0d5556fe9795b74714304564f2295dcfa24"; + hash = "sha256-w1wmJW7YwXyjvXJOWdN2+k+QmhXr4IflES/c2bCX3CI="; + }; + packageRequires = []; + }) + + ]; + }); + }; #+end_src *** Waybar +:PROPERTIES: +:CUSTOM_ID: h:0bf51f63-01c0-4053-a591-7f0c5697c690 +:END: Again I am just using the first bar option here that I was able to find good understandable documentation for. Of note is that the `cpu` section's `format` is not defined here, but in section 1 (since not every machine has the same number of cores) +The rest of this configuration is found here: +- [[#h:47749e76-3f25-485a-9e98-c7ce3a4ad444][Waybar items - PC]] +- [[#h:f3cf9bdc-6826-4d8e-ba5a-253ef098a9b8][Waybar items - LAPTOPS]] + #+begin_src nix :tangle profiles/common/home.nix programs.waybar = { @@ -6270,6 +6618,17 @@ Again I am just using the first bar option here that I was able to find good und #+end_src *** Firefox +:PROPERTIES: +:CUSTOM_ID: h:fbec0bd4-690b-4f79-8b2b-a40263760a96 +:END: + +Setting up firefox along with some policies that are important to me (mostlu disabling telemetry related stuff as well as Pocket). I also enable some integrations that enable super useful packages, namely =tridactyl= and =browserpass=. + +Also, using NUR with rycee's firefox addons, it is very convenient for me to add firefox addons here that will be automatically installed. + +Also, I setup some search aliases for functions I often use, such as NixOS options search (=@no=) + +I used to build the firefox addon =bypass-paywalls-clean= myself here, but the maintainer always deletes old packages, and it became a chore for me to maintain here, so I no longer do that. #+begin_src nix :tangle profiles/common/home.nix @@ -6316,13 +6675,6 @@ Again I am just using the first bar option here that I was able to find good und don-t-fuck-with-paste plasma-integration - # build the rest of my firefox addons myself - # app id can be found in the manifest.json file of the .xpi - # (.xpi is just a normal archive) - # url can be found by copy url of the "add extension" button on the addon page - # the rest of the information is also found in the manifest.json, but might not be - # needed - # (let version = "3.4.5.0"; # in buildFirefoxXpiAddon { # pname = "bypass-paywalls-clean"; @@ -6340,7 +6692,6 @@ Again I am just using the first bar option here that I was able to find good und # }; # }) - (buildFirefoxXpiAddon { pname = ":emoji:"; version = "0.1.3"; @@ -6409,24 +6760,19 @@ Again I am just using the first bar option here that I was able to find good und #+end_src -*** Browserpass - -#+begin_src nix :tangle profiles/common/home.nix - - # programs.browserpass = { - # enable = true; - # browsers = [ - # "firefox" - # ]; - # }; - -#+end_src - -** Services +*** Services +:PROPERTIES: +:CUSTOM_ID: h:387c3a82-1fb1-4c0f-8051-874e2acb8804 +:END: Services that can be defined through home-manager should be defined here. -*** General +**** gnome-keyring +:PROPERTIES: +:CUSTOM_ID: h:cb812c8a-247c-4ce5-a00c-59332c2f5fb9 +:END: + +Used for storing sessions in e.g. Nextcloud #+begin_src nix :tangle profiles/common/home.nix @@ -6434,15 +6780,47 @@ Services that can be defined through home-manager should be defined here. enable = true; }; - services.mbsync = { - enable = true; - }; + #+end_src +**** mbsync +:PROPERTIES: +:CUSTOM_ID: h:06d34282-5c75-4c21-a857-604f129ce911 +:END: - services.kdeconnect = { - enable = true; - indicator = true; - }; +Used for syncing mail. This might be automatically enabled by my mail configuration, but I like to make sure. + + #+begin_src nix :tangle profiles/common/home.nix + + services.mbsync = { + enable = true; + }; + + #+end_src + +**** KDE Connect +:PROPERTIES: +:CUSTOM_ID: h:be6afd89-9e1e-40b6-8542-5c07a0ab780d +:END: + +This enables phone/computer communication, including sending clipboard, files etc. Sadly on Wayland many of the features are broken (like remote control). + + #+begin_src nix :tangle profiles/common/home.nix + + services.kdeconnect = { + enable = true; + indicator = true; + }; + +#+end_src + +**** syncthing +:PROPERTIES: +:CUSTOM_ID: h:a17df4b8-1044-4569-b1b9-6c99ae354654 +:END: + +Enables the syncthing service which talks to my syncthing instance on the Oracle cloud as well as my home server. + +#+begin_src nix :tangle profiles/common/home.nix services.syncthing = { enable = true; @@ -6451,7 +6829,17 @@ Services that can be defined through home-manager should be defined here. }; }; - # this enables the emacs server +#+end_src + +**** Emacs server +:PROPERTIES: +:CUSTOM_ID: h:1f5a04de-0898-41ba-8134-741e4aeb0f79 +:END: + +This enables the Emacs server. This allows me to invocate Emacs by using =emacsclient= instead, which is a lot faster. + +#+begin_src nix :tangle profiles/common/home.nix + services.emacs = { enable = true; # socketActivation.enable = false; @@ -6459,7 +6847,12 @@ Services that can be defined through home-manager should be defined here. }; #+end_src -*** Mako +**** Mako +:PROPERTIES: +:CUSTOM_ID: h:99d05729-df35-4958-9940-3319d6a41359 +:END: + +Desktop notifications! The `extraConfig` section here CANNOT be reindented. This has something to do with how nix handles multiline strings, when indented Mako will fail to start. This might be a mako bug as well. @@ -6495,10 +6888,15 @@ The `extraConfig` section here CANNOT be reindented. This has something to do wi #+end_src -** Sway +*** Sway +:PROPERTIES: +:CUSTOM_ID: h:02df9dfc-d1af-4a37-a7a0-d8da0af96a20 +:END: I am currently using SwayFX, which adds some nice effects to sway, like rounded corners and hiding the separator between title and content of a window. +Currently, I am too lazy to explain every option here, but most of it is very self-explaining in any case. + #+begin_src nix :tangle profiles/common/home.nix wayland.windowManager.sway = { @@ -6659,12 +7057,6 @@ I am currently using SwayFX, which adds some nice effects to sway, like rounded class = ".*"; }; } - { - command = "opacity 0.95"; - criteria = { - app_id = ".*"; - }; - } { command = "opacity 1"; criteria = { @@ -6686,13 +7078,13 @@ I am currently using SwayFX, which adds some nice effects to sway, like rounded { command = "opacity 0.8, sticky enable, border normal, move container to scratchpad"; criteria = { - title="kittyterm"; + title="^kittyterm$"; }; } { command = "opacity 0.95, sticky enable, border normal, move container to scratchpad"; criteria = { - title="spotifytui"; + title="^spotifytui$"; }; } # { @@ -6787,46 +7179,19 @@ I am currently using SwayFX, which adds some nice effects to sway, like rounded #+end_src -* TODO Manual tasks, Useful bits, flake.nix skeleton and Closing Parenthesis (this needs to be the last heading) - -Here are listed some tasks that I was not able to automate yet, these need to be done automatically for now. Also, this section exists to add an extra closing parenthesis to common.nix so that I do not need to think about this anymore if I ever decide to add more headings towards the end of this file ;) - -- TODO: Check if the below tasks can be automated - -1) In blueman, toggle the `ConnectionNotifier` plugin to off (since it is highly annoing) - -The last blocks exist to close the opening parenthesis of modules/common.nix (home-manager) and profiles/common.nix (NixOS): -** Useful bits - -Here I have gathered some snippets that I have deleted from my configuration but figured they might be of use to someone still :) - -*** let-block for overriding a package in nixpkgs (here: replacing airsonic with airsonic-advanced) +** flake.nix template and Closing Parenthesis (this needs to be the last heading in the Systems header) +:PROPERTIES: +:CUSTOM_ID: h:aee5ec75-7ca6-40d8-b6ac-a3e7e33a474b +:END: -This can be useful if a module does not let you use your own package yourself. +This sections puts together the =flake.nix= file from the [[#h:d39b8dfb-536d-414f-9fc0-7d67df48cee4][Noweb-Ref blocks]] section. -#+begin_src nix +Here we also close the opening parenthesis of modules/common.nix (home-manager) and profiles/common.nix (NixOS): - pkgs = import nixpkgs { inherit system; - overlays = [ emacs-overlay.overlay - nur.overlay - nixgl.overlay - (self: super: { - airsonic = super.airsonic.overrideAttrs (_: rec { - version = "11.0.2-kagemomiji"; - name = "airsonic-advanced-${version}"; - src = super.fetchurl { - url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/11.0.2/airsonic.war"; - sha256 = "PgErtEizHraZgoWHs5jYJJ5NsliDd9VulQfS64ackFo="; - }; - }); - }) - ]; - config.allowUnfree = true; - }; - -#+end_src - -** Closing parentheses for common/home.nix and common/nixos.nix +*** Closing parentheses for common/home.nix and common/nixos.nix +:PROPERTIES: +:CUSTOM_ID: h:24e2a65b-b0cc-42cb-8e61-5a4cc39d6b2f +:END: #+begin_src nix :tangle profiles/common/home.nix @@ -6840,7 +7205,10 @@ This can be useful if a module does not let you use your own package yourself. #+end_src -** flake.nix +*** flake.nix +:PROPERTIES: +:CUSTOM_ID: h:4f89db68-a21c-415d-87a5-21c66f2b6ded +:END: This tangles the flake.nix file; This block only needs to be touched when updating the general structure of the flake. For everything else, see the respective noweb-ref block. @@ -6891,3 +7259,3231 @@ This tangles the flake.nix file; This block only needs to be touched when updati } #+end_src +* Emacs +:PROPERTIES: +:CUSTOM_ID: h:ed4cd05c-0879-41c6-bc39-3f1246a96f04 +:END: +** Initialization (early-init.el) +:PROPERTIES: +:CUSTOM_ID: h:2c331451-45ed-4592-9e00-d36b5bf31248 +:END: + +In this section I handle my early init file; it takes care of frame-setup for emacsclient buffers. + +*** Increase startup performance +:PROPERTIES: +:CUSTOM_ID: h:38e03b65-9dfc-4547-b27d-236664d7dc15 +:END: + +First, I use some advice from doomemacs regarding garbace collection; here I make sure that during startup, the garbace collectur will not run, which will improve startup times. Now, that might not really be needed since I will usually only start the emacs server once during startup and then not touch it again, however, since I am building my emacs configuration using NixOS, there is some merit to this since I will usually need to restart the server once I rebuild my configuration. + +Also, inspired by a setting I have seen in protesilaos' configuration, I apply the same idea to the =file-name-handler-alist= and =vc-handled-backends=. + +In the end, we need to restore those values to values that will work during normal operation. For that, I add a hook to the startup function that will revert the values once Emacs has finished initialization. + +Also packed into the hook function is the line =(fset 'epg-wait-for-status 'ignore)=. This line is needed at the end of the configuration in order to allow for my Yubikey to be used to encrypt and decrypt =.gpg= files. Without it, Emacs will just hang forever and basically crash. + +#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes + (defvar swarsel-file-name-handler-alist file-name-handler-alist) + (defvar swarsel-vc-handled-backends vc-handled-backends) + + (setq gc-cons-threshold most-positive-fixnum + gc-cons-percentage 0.6 + file-name-handler-alist nil + vc-handled-backends nil) + + (add-hook 'emacs-startup-hook + (lambda () + (progn + (setq gc-cons-threshold (* 1000 1000 8) + gc-cons-percentage 0.1 + file-name-handler-alist swarsel-file-name-handler-alist + vc-handled-backends swarsel-vc-handled-backends) + (fset 'epg-wait-for-status 'ignore) + ))) + +#+end_src +*** Setup frames +:PROPERTIES: +:CUSTOM_ID: h:782b3632-afb2-4c67-8c46-ff94408aef5d +:END: + +Next, I will setup the basic frame for my emacs buffers. Note that I use a tiling window manager, so I do not need to hold myself up with sizing the windows myself. I also disable some GUI tools that I (like many others) do not find to be particularly useful. Also I inhibit many startup functions here, even though it does not affect me greatly since I use another solution for that. + +We also make require immediate compilation of native code. + +For the =default-frame-alist=, I used to also set ='(right-divider-width . 4)= and ='(bottom-divider-width . 4)=, but I did not like the look of the divider bar and usually know my splits anyways, so this is no longer set. + +#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes + (tool-bar-mode 0) + (menu-bar-mode 0) + (scroll-bar-mode 0) + + (setq frame-inhibit-implied-resize t + ring-bell-function 'ignore + use-dialog-box nil + use-file-dialog nil + use-short-answers t + inhibit-startup-message t + inhibit-splash-screen t + inhibit-startup-screen t + inhibit-x-resources t + inhibit-startup-buffer-menu t + inhibit-startup-echo-area-message user-login-name ; this needs to be set to the username or it will not have an effect + comp-deferred-compilation nil ; compile all Elisp to native code immediately + ) + + (setq-default left-margin-width 1 + right-margin-width 1) + + (setq-default default-frame-alist + (append + (list + '(undecorated . t) ; no title bar, borders etc. + '(background-color . "#1D252C") ; load doom-citylight colors to avoid white flash + '(foreground-color . "#A0B3C5") ; load doom-citylight colors to avoid white flash + '(vertical-scroll-bars . nil) + '(horizontal-scroll-bars . nil) + '(internal-border-width . 5) + '(tool-bar-lines . 0) + '(menu-bar-lines . 0)))) + +#+end_src +*** Make C-i, C-m, C-[ available in graphic sessions +:PROPERTIES: +:CUSTOM_ID: h:396c47f2-7e2f-4fad-ae71-6483bf7e3e42 +:END: + +By default, emacs binds +- =C-i= to the =TAB= key +- =C-m= to the =RET= key +- =C-[= to the =ECS= key + +These keybinds exist to make Emacs work well in terminal mode. However, most of the time I am using Emacs in a graphic session, and I would hence like to have these keybinds available for personal use. + +NOTE: To use these keybinds, you need to enclose the binding in angled brackets (=<>=). Then they can be used normally + +#+begin_src emacs-lisp :tangle programs/emacs/early-init.el :mkdirp yes + + (add-hook + 'after-make-frame-functions + (lambda (frame) + (with-selected-frame frame + (when (display-graphic-p) + (define-key input-decode-map (kbd "C-i") [DUMMY-i]) + (define-key input-decode-map (kbd "C-[") [DUMMY-lsb]) + (define-key input-decode-map (kbd "C-m") [DUMMY-m]) + )))) + + + + +#+end_src +** Personal settings +:PROPERTIES: +:CUSTOM_ID: h:601ba407-b906-4869-8ef6-67a9fc285fba +:END: + +This section is used to define my own functions, own variables, and own keybindings. + +*** Custom functions +:PROPERTIES: +:CUSTOM_ID: h:b7b5976a-db2b-493d-8794-1924a0e12aec +:END: + +In this section I define extra functions that I need. Some of these functions I wrote myself, some I found after internet reseach. For functions I found on the internet, I will link the original source I found it in. + +**** Emacs/Evil state toggle +:PROPERTIES: +:CUSTOM_ID: h:b715d7cf-da09-4e0c-95bc-8281b4f3ce9c +:END: + +Since I am rebinding the =C-z= hotkey for emacs-evil-state toggling, I want to have a function that still lets me perform this action quickly. + +#+begin_src emacs-lisp + + (defun swarsel/toggle-evil-state () + (interactive) + (if (or (evil-emacs-state-p) (evil-insert-state-p)) + (evil-normal-state) + (evil-emacs-state))) + +#+end_src + +**** Switching to last used buffer +:PROPERTIES: +:CUSTOM_ID: h:1e0ee570-e509-4ecb-a3af-b75543731bb0 +:END: + +I often find myself bouncing between two buffers when I do not want to use a window split. This funnction simply jumps to the last used buffer. + +#+begin_src emacs-lisp + + (defun swarsel/last-buffer () (interactive) (switch-to-buffer nil)) + +#+end_src +**** mu4e functions +:PROPERTIES: +:CUSTOM_ID: h:34506761-06b9-43b5-a818-506d9b3faf28 +:END: + +I use these functions to let me switch between my main email accounts, as mu4e by itself has trouble doing so. =mu4e-switch-account= allows for manual choosing of the sender account, while =mu4e-rfs--matching-address= and =mu4e-send-from-correct-address= are used when replying to a mail; they switch the sender account to the one that received the mail. + +By default, the sender email will not be changed after sending a mail; however, I want Emacs to always use my main address when not replying to another email. For that I use =mu4e-restore-default=. + +Used here: [[#h:b92a18cf-eec3-4605-a8c2-37133ade3574][mu4e]] + +#+begin_src emacs-lisp + + (defun swarsel/mu4e-switch-account () + (interactive) + (let ((account (completing-read "Select account: " mu4e-user-mail-address-list))) + (setq user-mail-address account))) + + (defun swarsel/mu4e-rfs--matching-address () + (cl-loop for to-data in (mu4e-message-field mu4e-compose-parent-message :to) + for to-email = (pcase to-data + (`(_ . email) email) + (x (mu4e-contact-email x))) + for to-name = (pcase to-data + (`(_ . name) name) + (x (mu4e-contact-name x))) + when (mu4e-user-mail-address-p to-email) + return (list to-name to-email))) + + (defun swarsel/mu4e-send-from-correct-address () + (when mu4e-compose-parent-message + (save-excursion + (when-let ((dest (swarsel/mu4e-rfs--matching-address))) + (cl-destructuring-bind (from-user from-addr) dest + (setq user-mail-address from-addr) + (message-position-on-field "From") + (message-beginning-of-line) + (delete-region (point) (line-end-position)) + (insert (format "%s <%s>" (or from-user user-full-name) from-addr))))))) + + (defun swarsel/mu4e-restore-default () + (setq user-mail-address "leon@swarsel.win" + user-full-name "Leon Schwarzäugl")) + + +#+end_src +**** Create non-existant directories when finding file +:PROPERTIES: +:CUSTOM_ID: h:4b9d74ef-0376-45bb-bc15-d24a04ca7e81 +:END: + +This function will check if a directory for which a file we want to open exists; if not, it will offer to create the directories for me. + +#+begin_src emacs-lisp + + (defun swarsel/with-buffer-name-prompt-and-make-subdirs () + (let ((parent-directory (file-name-directory buffer-file-name))) + (when (and (not (file-exists-p parent-directory)) + (y-or-n-p (format "Directory `%s' does not exist! Create it? " parent-directory))) + (make-directory parent-directory t)))) + + (add-to-list 'find-file-not-found-functions #'swarsel/with-buffer-name-prompt-and-make-subdirs) + +#+end_src +**** [crux] Duplicate Lines +:PROPERTIES: +:CUSTOM_ID: h:91e2f45f-54fa-4b60-8758-b2ef9b439af7 +:END: + +When programming, I like to be able to duplicate a line. There are easier functions than the one below, but they either + +1) screw with undo/redo +2) move the cursor wildly + +The below function avoids these problems. Originally I used the function =duplicate-line= found here: [[https://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs]] + +However, this function does not work on regions. Later, I found a solution implemented by [[https://github.com/bbatsov/crux][crux]]. I do not need the whole package, so I just extracted the three functions I needed from it. + +#+begin_src emacs-lisp + + (defun crux-get-positions-of-line-or-region () + "Return positions (beg . end) of the current line or region." + (let (beg end) + (if (and mark-active (> (point) (mark))) + (exchange-point-and-mark)) + (setq beg (line-beginning-position)) + (if mark-active + (exchange-point-and-mark)) + (setq end (line-end-position)) + (cons beg end))) + + (defun crux-duplicate-current-line-or-region (arg) + "Duplicates the current line or region ARG times. + If there's no region, the current line will be duplicated. However, if + there's a region, all lines that region covers will be duplicated." + (interactive "p") + (pcase-let* ((origin (point)) + (`(,beg . ,end) (crux-get-positions-of-line-or-region)) + (region (buffer-substring-no-properties beg end))) + (dotimes (_i arg) + (goto-char end) + (newline) + (insert region) + (setq end (point))) + (goto-char (+ origin (* (length region) arg) arg)))) + + (defun crux-duplicate-and-comment-current-line-or-region (arg) + "Duplicates and comments the current line or region ARG times. + If there's no region, the current line will be duplicated. However, if + there's a region, all lines that region covers will be duplicated." + (interactive "p") + (pcase-let* ((origin (point)) + (`(,beg . ,end) (crux-get-positions-of-line-or-region)) + (region (buffer-substring-no-properties beg end))) + (comment-or-uncomment-region beg end) + (setq end (line-end-position)) + (dotimes (_ arg) + (goto-char end) + (newline) + (insert region) + (setq end (point))) + (goto-char (+ origin (* (length region) arg) arg)))) + +#+end_src +**** [prot] org-id-headings +:PROPERTIES: +:CUSTOM_ID: h:4819720a-9220-4c34-b903-ed4179f3ad1c +:END: + +These functions by protesilaos generate heading links in an org-file similar to the normal =org-store-link= approach when not using properties. This approach has a weakness however - if the heading name is changed, the link breaks. These functions generate a unique identifier for each heading which will not break and also works when exporting the file to html, for example. + +#+begin_src emacs-lisp + + (defun prot-org--id-get () + "Get the CUSTOM_ID of the current entry. + If the entry already has a CUSTOM_ID, return it as-is, else + create a new one." + (let* ((pos (point)) + (id (org-entry-get pos "CUSTOM_ID"))) + (if (and id (stringp id) (string-match-p "\\S-" id)) + id + (setq id (org-id-new "h")) + (org-entry-put pos "CUSTOM_ID" id) + id))) + + (declare-function org-map-entries "org") + + (defun prot-org-id-headlines () + "Add missing CUSTOM_ID to all headlines in current file." + (interactive) + (org-map-entries + (lambda () (prot-org--id-get)))) + + (defun prot-org-id-headline () + "Add missing CUSTOM_ID to headline at point." + (interactive) + (prot-org--id-get)) + +#+end_src +**** Inhibit Messages in Echo Area +:PROPERTIES: +:CUSTOM_ID: h:285e5c0a-875d-46a8-bb9b-0222b3d73878 +:END: + +Emacs likes to send messages to the echo area; this is generally a good thing. However, it bothers me a lot when I am currently working in minibuffer where I receive an echo area message that is actually important and it is then overwritten by e.g. the mu4e update message. This section makes it possible to find the root function calling the message function and disabling it here. + +Usage: Enable the =(advice-add 'message :around #'who-called-me?)= by running this code block, which will show a full trace of all messages being sent to the echo area: + +#+begin_src emacs-lisp :tangle no :export both :results silent + + (advice-add 'message :around #'who-called-me?) + +#+end_src + +Once the root function has been found, it can be disabled via =advice=add= as in the last block in this section. To disable the stack tracing, run =(advice-remove 'message #'who-called-me?)= or the following code block: + +#+begin_src emacs-lisp :tangle no :results silent + + (advice-remove 'message #'who-called-me?) + +#+end_src + +Lastly, individual messages can be reenabled using the =(advice-remove ' #'suppress-messages)= approach. Use this when you accidentally disabled a helpful message. + + +#+begin_src emacs-lisp + + (defun suppress-messages (old-fun &rest args) + (cl-flet ((silence (&rest args1) (ignore))) + (advice-add 'message :around #'silence) + (unwind-protect + (apply old-fun args) + (advice-remove 'message #'silence)))) + + (advice-add 'pixel-scroll-precision :around #'suppress-messages) + (advice-add 'mu4e--server-filter :around #'suppress-messages) + (advice-add 'org-unlogged-message :around #'suppress-messages) + (advice-add 'magit-auto-revert-mode--init-kludge :around #'suppress-messages) + (advice-add 'push-mark :around #'suppress-messages) + + ;; to reenable + ;; (advice-remove 'timer-event-handler #'suppress-messages) + + (defun who-called-me? (old-fun format &rest args) + (let ((trace nil) (n 1) (frame nil)) + (while (setf frame (backtrace-frame n)) + (setf n (1+ n) + trace (cons (cadr frame) trace)) ) + (apply old-fun (concat "<<%S>>\n" format) (cons trace args)))) + + ;; enable to get message backtrace, the first function shown in backtrace calls the other functions + ;; (advice-add 'message :around #'who-called-me?) + + ;; disable to stop receiving backtrace + (advice-remove 'message #'who-called-me?) + + +#+end_src +**** Move up one directory for find-file +:PROPERTIES: +:CUSTOM_ID: h:de249f2a-6a2b-4114-8046-09d1014a7391 +:END: + +I find it very annoying that the standard behavior for M-DEL only deletes one word when using find-file. This function makes it so that we always go up by one directory level instead. + +This function was found here: [[https://www.reddit.com/r/emacs/comments/re31i6/how_to_go_up_one_directory_when_using_findfile_cx/]] + +#+begin_src emacs-lisp + + (defun up-directory (path) + "Move up a directory in PATH without affecting the kill buffer." + (interactive "p") + (if (string-match-p "/." (minibuffer-contents)) + (let ((end (point))) + (re-search-backward "/.") + (forward-char) + (delete-region (point) end)))) + + (define-key minibuffer-local-filename-completion-map + [C-backspace] #'up-directory) + +#+end_src +**** org-mode: General setup +:PROPERTIES: +:CUSTOM_ID: h:06b77d28-3fd5-4554-8c7d-32c1b0ec8da5 +:END: + +Sets up the basic settings that I want to have active in org-mode buffers. + +Used here: [[#h:877c9401-a354-4e44-a235-db1a90d19e00][General org-mode]] + +#+begin_src emacs-lisp + + (defun swarsel/org-mode-setup () + (org-indent-mode) + (variable-pitch-mode 1) + ;;(auto-fill-mode 0) + (setq display-line-numbers-type 'relative + display-line-numbers-current-absolute 1 + display-line-numbers-width-start nil + display-line-numbers-width 6 + display-line-numbers-grow-only 1) + (add-hook 'org-tab-first-hook 'org-end-of-line) + (visual-line-mode 1)) + +#+end_src +**** org-mode: Visual-fill column +:PROPERTIES: +:CUSTOM_ID: h:fa710375-2efe-49b4-af6a-a875aca6e4a2 +:END: + +This function sets the width of buffers in org-mode. + +Used in: [[#h:bbcfa895-4d46-4b1d-b84e-f634e982c46e][Centered org-mode Buffers]] + +#+begin_src emacs-lisp + + (defun swarsel/org-mode-visual-fill () + (setq visual-fill-column-width 150 + visual-fill-column-center-text t) + (visual-fill-column-mode 1)) + +#+end_src + +**** org-mode: Auto-tangle and export Configuration Files +:PROPERTIES: +:CUSTOM_ID: h:59d4306e-9b73-4b2c-b039-6a6518c357fc +:END: + +This section automatically tangles all configuration blocks in this file to the defined Emacs org-file. It also exports the configuration file as html. + + + +#+begin_src emacs-lisp + + (defun swarsel/org-babel-tangle-config () + (when (string-equal (buffer-file-name) + swarsel-swarsel-org-filepath) + ;; Dynamic scoping to the rescue + (let ((org-confirm-babel-evaluate nil)) + (org-html-export-to-html) + (org-babel-tangle))) + (when (string-equal (buffer-file-name) + swarsel-emacs-org-filepath) + ;; Dynamic scoping to the rescue + (let ((org-confirm-babel-evaluate nil)) + (org-html-export-to-html) + (org-babel-tangle))) + (when (string-equal (buffer-file-name) + swarsel-nix-org-filepath) + ;; Dynamic scoping to the rescue + (let ((org-confirm-babel-evaluate nil)) + (org-babel-tangle)))) + + (setq org-html-htmlize-output-type nil) + + (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config))) + + + +#+end_src + +**** org-mode: Fold current heading +:PROPERTIES: +:CUSTOM_ID: h:dfa66e78-5748-45e3-a975-db3da104bb3a +:END: + +Normally emacs cycles between three states: + +1) fully folded +2) One heading expanded +3) All headings expanded + +However, I want to be able to fold a single heading consistently. + +#+begin_src emacs-lisp + +(defun org-fold-outer () + (interactive) + (org-beginning-of-line) + (if (string-match "^*+" (thing-at-point 'line t)) + (outline-up-heading 1)) + (outline-hide-subtree) + ) + +#+end_src + +**** corfu: Do not interrupt navigation +:PROPERTIES: +:CUSTOM_ID: h:a1802f9b-bb71-4fd5-86fa-945da18e8b81 +:END: + +These three functions allow me to keep using the normal navigation keys even when a corfu completion pops up. + +These functions are used here: [[#h:5653d693-ecca-4c95-9633-66b9e3241070][Corfu]] + +#+begin_src emacs-lisp + + (defun swarsel/corfu-normal-return (&optional arg) + (interactive) + (corfu-quit) + (newline) + ) + + (defun swarsel/corfu-quit-and-up (&optional arg) + (interactive) + (corfu-quit) + (evil-previous-visual-line)) + + (defun swarsel/corfu-quit-and-down (&optional arg) + (interactive) + (corfu-quit) + (evil-next-visual-line)) + +#+end_src + +**** python shell reloading +:PROPERTIES: +:CUSTOM_ID: h:291e43d9-eae2-4e23-8e38-160e223bf314 +:END: + +The standard Emacs behaviour for the Python process shell is a bit annoying. This is my attempt at making it show automatically on opening a python buffer and making it refresh on its own as well. This does not nicely work yet. + +#+begin_src emacs-lisp + + ;; run the python inferior shell immediately upon entering a python buffer + ;; (add-hook 'python-mode-hook 'swarsel/run-python) + + ;; (defun swarsel/run-python () + ;; (save-selected-window + ;; (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command)))))) + + ;; reload python shell automatically + (defun my-python-shell-run () + (interactive) + (when (get-buffer-process "*Python*") + (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil) + (kill-process (get-buffer-process "*Python*")) + ;; Uncomment If you want to clean the buffer too. + ;;(kill-buffer "*Python*") + ;; Not so fast! + (sleep-for 0.5)) + (run-python (python-shell-parse-command) nil nil) + (python-shell-send-buffer) + ;; Pop new window only if shell isnt visible + ;; in any frame. + (unless (get-buffer-window "*Python*" t) + (python-shell-switch-to-shell))) + + (defun my-python-shell-run-region () + (interactive) + (python-shell-send-region (region-beginning) (region-end)) + (python-shell-switch-to-shell)) + +#+end_src + +*** Custom Keybindings +:PROPERTIES: +:CUSTOM_ID: h:2b827c27-0de7-45ed-9d9e-6c511e2c6bb5 +:END: + +This defines a set of keybinds that I want to have available globally. I have one set of keys that is globally available through the =C-SPC= prefix. This set is used mostly for functions that I have trouble remembering the original keybind for, or that I just want to have gathered in a common space. + +I also define some keybinds to some combinations directly. Those are used mostly for custom functions that I call often enough to warrant this. + +#+begin_src emacs-lisp + + ;; Make ESC quit prompts + (global-set-key (kbd "") 'keyboard-escape-quit) + + ;; Set up general keybindings + (use-package general + :config + (general-create-definer swarsel/leader-keys + :keymaps '(normal insert visual emacs) + :prefix "SPC" + :global-prefix "C-SPC") + + (swarsel/leader-keys + "e" '(:ignore e :which-key "evil") + "eo" '(evil-jump-backward :which-key "cursor jump backwards") + "eO" '(evil-jump-forward :which-key "cursor jump forwards") + "t" '(:ignore t :which-key "toggles") + "ts" '(hydra-text-scale/body :which-key "scale text") + "te" '(swarsel/toggle-evil-state :which-key "emacs/evil") + "tl" '(display-line-numbers-mode :which-key "line numbers") + "tp" '(evil-cleverparens-mode :wk "cleverparens") + "to" '(olivetti-mode :wk "olivetti") + "td" '(darkroom-tentative-mode :wk "darkroom") + "tw" '((lambda () (interactive) (toggle-truncate-lines)) :which-key "line wrapping") + "m" '(:ignore m :which-key "modes/programs") + "mm" '((lambda () (interactive) (mu4e)) :which-key "mu4e") + "mg" '((lambda () (interactive) (magit-list-repositories)) :which-key "magit-list-repos") + "mc" '((lambda () (interactive) (swarsel/open-calendar)) :which-key "calendar") + "mp" '(popper-toggle :which-key "popper") + "md" '(dirvish :which-key "dirvish") + "o" '(:ignore o :which-key "org") + "op" '((lambda () (interactive) (org-present)) :which-key "org-present") + "ob" '((lambda () (interactive) (org-babel-mark-block)) :which-key "Mark whole src-block") + "ol" '((lambda () (interactive) (org-insert-link)) :which-key "insert link") + "os" '((lambda () (interactive) (org-store-link)) :which-key "store link") + "od" '((lambda () (interactive) (org-babel-demarcate-block)) :which-key "demarcate (split) src-block") + ;; "c" '(:ignore c :which-key "capture") + ;; "cj" '((lambda () (interactive) (org-capture nil "jj")) :which-key "journal") + ;; "cs" '(markdown-download-screenshot :which-key "screenshot") + "l" '(:ignore l :which-key "links") + "lc" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (org-overview) )) :which-key "SwarselSystems.org") + "le" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "Emacs") ) (org-overview) (org-cycle) )) :which-key "Emacs.org") + "ln" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "System") ) (org-overview) (org-cycle))) :which-key "Nixos.org") + "ls" '((lambda () (interactive) (find-file "/smb:Swarsel@192.168.1.3:")) :which-key "Server") + "lo" '(dired swarsel-obsidian-vault-directory :which-key "obsidian") + ;; "la" '((lambda () (interactive) (find-file swarsel-org-anki-filepath)) :which-key "anki") + ;; "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org") + "lp" '((lambda () (interactive) (projectile-switch-project)) :which-key "switch project") + "lg" '((lambda () (interactive) (magit-list-repositories)) :which-key "list git repos") + ;; "a" '(:ignore a :which-key "anki") + ;; "ap" '(anki-editor-push-tree :which-key "push new cards") + ;; "an" '((lambda () (interactive) (org-capture nil "a")) :which-key "new card") + ;; "as" '(swarsel-anki-set-deck-and-notetype :which-key "change deck and notetype") + "h" '(:ignore h :which-key "help") + "hy" '(yas-describe-tables :which-key "yas tables") + "hb" '(embark-bindings :which-key "current key bindings") + "h" '(:ignore t :which-key "describe") + "he" 'view-echo-area-messages + "hf" 'describe-function + "hF" 'describe-face + "hl" '(view-lossage :which-key "show command keypresses") + "hL" 'find-library + "hm" 'describe-mode + "ho" 'describe-symbol + "hk" 'describe-key + "hK" 'describe-keymap + "hp" 'describe-package + "hv" 'describe-variable + "hd" 'devdocs-lookup + "w" '(:ignore t :which-key "window") + "wl" 'windmove-right + "wh" 'windmove-left + "wk" 'windmove-up + "wj" 'windmove-down + "wr" 'winner-redo + "wd" 'delete-window + "w=" 'balance-windows-area + "wD" 'kill-buffer-and-window + "wu" 'winner-undo + "wr" 'winner-redo + "w/" 'evil-window-vsplit + "w-" 'evil-window-split + "wm" '(delete-other-windows :wk "maximize") + )) + + ;; General often used hotkeys + (general-define-key + "C-M-a" (lambda () (interactive) (org-capture nil "a")) ; make new anki card + ;; "C-M-d" 'swarsel-obsidian-daily ; open daily obsidian file and create if not exist + ;; "C-M-S" 'swarsel-anki-set-deck-and-notetype ; switch deck and notetype for new anki cards + ;; "C-M-s" 'markdown-download-screenshot ; wrapper for org-download-screenshot + "C-c d" 'crux-duplicate-current-line-or-region + "C-c D" 'crux-duplicate-and-comment-current-line-or-region + "" 'swarsel/last-buffer + "M-\\" 'indent-region + "C-" 'my-python-shell-run + ) + +#+end_src +*** Directory setup / File structure +:PROPERTIES: +:CUSTOM_ID: h:07951589-54ba-4e3e-bd7b-4106cd22ff6a +:END: + +In this section I setup some aliases that I use for various directories on my system. Some of these are actually used for magit repository finding etc., but many of them serve no real use and I need to clean this up someday. + +#+begin_src emacs-lisp + + ;; set Nextcloud directory for journals etc. + (setq swarsel-sync-directory "~/Nextcloud" + swarsel-emacs-directory "~/.emacs.d" + swarsel-dotfiles-directory "~/.dotfiles" + swarsel-projects-directory "~/Documents/GitHub") + + (setq swarsel-emacs-org-filepath (expand-file-name "Emacs.org" swarsel-dotfiles-directory) + swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory) + swarsel-swarsel-org-filepath (expand-file-name "SwarselSystems.org" swarsel-dotfiles-directory) + ) + + + ;; set Emacs main configuration .org names + (setq swarsel-emacs-org-file "Emacs.org" + swarsel-anki-org-file "Anki.org" + swarsel-tasks-org-file "Tasks.org" + swarsel-archive-org-file "Archive.org" + swarsel-org-folder-name "Org" + swarsel-obsidian-daily-folder-name "⭐ Personal/Journal" + swarsel-obsidian-folder-name "Obsidian" + swarsel-obsidian-vault-name "Main") + + + ;; set directory paths + (setq swarsel-org-directory (expand-file-name swarsel-org-folder-name swarsel-sync-directory)) ; path to org folder + (setq swarsel-obsidian-directory (expand-file-name swarsel-obsidian-folder-name swarsel-sync-directory)) ; path to obsidian + (setq swarsel-obsidian-vault-directory (expand-file-name swarsel-obsidian-vault-name swarsel-obsidian-directory)) ; path to obsidian vault + (setq swarsel-obsidian-daily-directory (expand-file-name swarsel-obsidian-daily-folder-name swarsel-obsidian-vault-directory)) ; path to obsidian daily folder + + ;; filepaths to certain documents + (setq swarsel-org-anki-filepath (expand-file-name swarsel-anki-org-file swarsel-org-directory) ; path to anki export file + swarsel-org-tasks-filepath (expand-file-name swarsel-tasks-org-file swarsel-org-directory) + swarsel-org-archive-filepath (expand-file-name swarsel-archive-org-file swarsel-org-directory)) + + + +#+end_src + +*** Unclutter .emacs.d +:PROPERTIES: +:CUSTOM_ID: h:0cf30b76-91d9-41da-a10b-74199bc36d40 +:END: + +In this section I move the =custom.el= out of it's standard location in =.emacs.d=. Firstly, I dislike using this file at all since I would rather have fully stateful configuration as commanded by this file. Secondly, this file is too easily permanently changed. Recently I figured out the last bits that I needed to remove from custom.el to no longer be reliant on it, so I now just write it to a temporary file (through =make-temp=file=) which will be cleaned on shutdown. However, I like to retain the custom framework because it is nice for testing out theme customizations, hence why I still load the file. + +This section also sets the emacs directory to the =~/.cache/= directory which is useful for files that I do not want to have lying around in my =.emacs.d=. + +#+begin_src emacs-lisp + + ;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d + (setq user-emacs-directory (expand-file-name "~/.cache/emacs/") + url-history-file (expand-file-name "url/history" user-emacs-directory)) + + ;; Use no-littering to automatically set common paths to the new user-emacs-directory + (use-package no-littering) + (setq custom-file (make-temp-file "emacs-custom-")) + (load custom-file t) + +#+end_src + +*** Move backup files to another location +:PROPERTIES: +:CUSTOM_ID: h:329f529a-ef9f-4787-b311-1c485e05b754 +:END: + +Many people dislike the Emacs backup files; I do enjoy them, but have to admit that they clutter the filesystem a little too much. Also, I rarely need to access these over different sessions. Hence I move them to =/tmp= - if Emacs unexpectedly crashes, the files can be recovered, but the backup files will not gather everywhere and will be deleted upon shutdown. + +#+begin_src emacs-lisp + + (let ((backup-dir "~/tmp/emacs/backups") + (auto-saves-dir "~/tmp/emacs/auto-saves/")) + (dolist (dir (list backup-dir auto-saves-dir)) + (when (not (file-directory-p dir)) + (make-directory dir t))) + (setq backup-directory-alist `(("." . ,backup-dir)) + auto-save-file-name-transforms `((".*" ,auto-saves-dir t)) + auto-save-list-file-prefix (concat auto-saves-dir ".saves-") + tramp-backup-directory-alist `((".*" . ,backup-dir)) + tramp-auto-save-directory auto-saves-dir)) + + (setq backup-by-copying t ; Don't delink hardlinks + delete-old-versions t ; Clean up the backups + version-control t ; Use version numbers on backups, + kept-new-versions 5 ; keep some new versions + kept-old-versions 2) ; and some old ones, too + +#+end_src +** General init.el setup + UI +:PROPERTIES: +:CUSTOM_ID: h:786b447d-03ad-4c1d-b114-c37caa2d591c +:END: + +In this general section I have settings that I either consider to be integral to my experience when using emacs or have no other section that I feel they belong to. + +*** General setup +:PROPERTIES: +:CUSTOM_ID: h:76a5bd78-a20d-4068-bea8-a38fdb26428e +:END: + +Here I set up some things that are too minor to put under other categories. +- Firstly we disable to having to type `yes` and `no` and switch it to `y` and `n`. +- We also enable the marking of trailing whitespaces. +- Also, make emacs highlight the current line globally +- Emacs defaults to pausing all display redrawing on any input. This may have been useful previously, but is not necessary nowadays. +- I also disable the suspend-frame function, as I never use it and it is quite confusing when accidentally hitting the keys for it. + +#+begin_src emacs-lisp + + ;; use UTF-8 everywhere + (set-language-environment "UTF-8") + + ;; set default font size + (defvar swarsel/default-font-size 130) + (setq swarsel-standard-font "FiraCode Nerd Font Mono" + swarsel-alt-font "FiraCode Nerd Font Mono") + + ;; (defalias 'yes-or-no-p 'y-or-n-p) + ;;(setq-default show-trailing-whitespace t) + (add-hook 'before-save-hook 'delete-trailing-whitespace) + (global-hl-line-mode 1) + ;; (setq redisplay-dont-pause t) ;; obsolete + (setq blink-cursor-mode nil) ;; blink-cursor is an unexpected source of slowdown + (global-subword-mode 1) ; Iterate through CamelCase words + (setq blink-matching-paren nil) ;; this makes the cursor jump around annoyingly + (delete-selection-mode 1) + (setq vc-follow-symlinks t) + (setq require-final-newline t) + (winner-mode 1) + (setq load-prefer-newer t) + + (setq undo-limit 80000000 + evil-want-fine-undo t + auto-save-default t + password-cache-expiry nil + ) + (setq browse-url-browser-function 'browse-url-firefox) + ;; disable a keybind that does more harm than good + (global-set-key [remap suspend-frame] + (lambda () + (interactive) + (message "This keybinding is disabled (was 'suspend-frame')"))) + + (setq visible-bell nil) + (setq initial-major-mode 'fundamental-mode + initial-scratch-message nil) + + (add-hook 'prog-mode-hook 'display-line-numbers-mode) + (add-hook 'text-mode-hook 'display-line-numbers-mode) + +#+end_src + +*** Mark all themes as safe +:PROPERTIES: +:CUSTOM_ID: h:0debe8fd-b319-4ab7-a92c-784fa7896b75 +:END: + +Normally when switching themes in emacs, the user will be warned that themes can run malicious code. I only run one theme really and deem it safe. It is however annoying to be asked this on every new system and it also creates lines in custom.el to answer that query, so here I declare all themes as safe. + +#+begin_src emacs-lisp + + (setq custom-safe-themes t) + +#+end_src +*** Show less compilation warnings +:PROPERTIES: +:CUSTOM_ID: h:b587e869-9911-443b-bc6d-8fb3ce31908d +:END: + +When Emacs compiles stuff, it often shows a bunch of warnings that I do not need to deal with. Here we silence those. Some will be disabled completely, and some only when we have native compilation available (which should be most of the time, however). + +#+begin_src emacs-lisp + + (setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) + ;; Make native compilation silent and prune its cache. + (when (native-comp-available-p) + (setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation + (setq native-compile-prune-cache t)) ; Emacs 29 + +#+end_src + +*** Indentation +:PROPERTIES: +:CUSTOM_ID: h:6527b3ce-b76d-431a-9960-a57da7c53e1b +:END: + +Here I define several options related to indentation; I first make it so that only whitespace will be used instead of tab characters for indentation, and I also set a small standard indent. + +We set =tab-always-indent= to ='complete= in order to indent first and then do completion if there are any. Also we make it so that python will not complain about missing indentation info. + +Lastly, I load the =highlight-indent-guides= package. This adds a neat visual indicator of the indentation level, which is useful for languages like python. + +#+begin_src emacs-lisp + + (setq-default indent-tabs-mode nil + tab-width 2) + + (setq tab-always-indent 'complete) + (setq python-indent-guess-indent-offset-verbose nil) + + (use-package highlight-indent-guides + :hook (prog-mode . highlight-indent-guides-mode) + :init + (setq highlight-indent-guides-method 'column) + (setq highlight-indent-guides-responsive 'top) + ) + + (with-eval-after-load 'highlight-indent-guides + (set-face-attribute 'highlight-indent-guides-even-face nil :background "gray10") + (set-face-attribute 'highlight-indent-guides-odd-face nil :background "gray20") + (set-face-attribute 'highlight-indent-guides-stack-even-face nil :background "gray40") + (set-face-attribute 'highlight-indent-guides-stack-odd-face nil :background "gray50")) +#+end_src + +*** Scrolling +:PROPERTIES: +:CUSTOM_ID: h:3dc9fb1d-cd16-4bd0-a9ac-55a944415a90 +:END: + +By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying. This sets up more granular scrolling that allows scrolling with a mouse wheel or the two-finger touchscreen gesture. This now also works in buffers with a very small frame. + +#+begin_src emacs-lisp + + (setq mouse-wheel-scroll-amount + '(1 + ((shift) . 5) + ((meta) . 0.5) + ((control) . text-scale)) + mouse-drag-copy-region nil + make-pointer-invisible t + mouse-wheel-progressive-speed t + mouse-wheel-follow-mouse t) + + (setq-default scroll-preserve-screen-position t + scroll-conservatively 1 + scroll-margin 0 + next-screen-context-lines 0) + + (pixel-scroll-precision-mode 1) + +#+end_src + +*** Evil +:PROPERTIES: +:CUSTOM_ID: h:5bf9f014-ee96-42da-b285-7b34f04e6bb1 +:END: + +**** General evil +:PROPERTIES: +:CUSTOM_ID: h:218376e8-086b-46bf-91b3-78295d5d440f +:END: + +This setups up evil, which brings vim-like keybindings to emacs. In the same location, I also unbind the =C-z= key (I am very unhappy with this implementation, but it is the only thing that works consistently so far) to make it available for [[#h:c3cc1c12-3ab8-42b7-be07-63f54eac397f][cape]] later. + +Also, I setup initial modes for several major-modes depending on what I deem fit. + +#+begin_src emacs-lisp + + ;; Emulate vim in emacs + (use-package evil + :init + (setq evil-want-integration t) ; loads evil + (setq evil-want-keybinding nil) ; loads "helpful bindings" for other modes + (setq evil-want-C-u-scroll t) ; scrolling using C-u + (setq evil-want-C-i-jump nil) ; jumping with C-i + (setq evil-want-Y-yank-to-eol t) ; give Y some utility + (setq evil-shift-width 2) ; uniform indent + (setq evil-respect-visual-line-mode t) ; i am torn on this one + (setq evil-split-window-below t) + (setq evil-vsplit-window-right t) + :config + (evil-mode 1) + (define-key evil-normal-state-map (kbd "C-z") nil) + (define-key evil-insert-state-map (kbd "C-z") nil) + (define-key evil-visual-state-map (kbd "C-z") nil) + (define-key evil-motion-state-map (kbd "C-z") nil) + (define-key evil-operator-state-map (kbd "C-z") nil) + (define-key evil-replace-state-map (kbd "C-z") nil) + (define-key global-map (kbd "C-z") nil) + (evil-set-undo-system 'undo-tree) + + ;; Don't use evil-mode in these contexts, or use it in a specific mode + (evil-set-initial-state 'messages-buffer-mode 'emacs) + (evil-set-initial-state 'dashboard-mode 'emacs) + (evil-set-initial-state 'dired-mode 'emacs) + (evil-set-initial-state 'cfw:details-mode 'emacs) + (evil-set-initial-state 'Custom-mode 'emacs) ; god knows why this mode is in uppercase + (evil-set-initial-state 'mu4e-headers-mode 'normal) + (evil-set-initial-state 'python-inferior-mode 'normal) + (add-hook 'org-capture-mode-hook 'evil-insert-state) + (add-to-list 'evil-buffer-regexps '("COMMIT_EDITMSG" . insert))) + +#+end_src +**** evil-collection +:PROPERTIES: +:CUSTOM_ID: h:bde208f3-01ef-4dc6-9981-65f3d2a8189b +:END: + +This gives support for many different modes, and works beautifully out of the box. + +#+begin_src emacs-lisp + + (use-package evil-collection + :after evil + :config + (evil-collection-init) + (setq forge-add-default-bindings nil)) +#+end_src +**** evil-snipe +:PROPERTIES: +:CUSTOM_ID: h:d80e3f7d-0185-4a15-832b-d756e576265c +:END: + +This package changes the char-search commands like =f= by showing the results in a more visual manner. It also gives a 2-character search using =s= and =S=. + +#+begin_src emacs-lisp + ;; enables 2-char inline search + (use-package evil-snipe + :after evil + :demand + :config + (evil-snipe-mode +1) + ;; replace 1-char searches (f&t) with this better UI + (evil-snipe-override-mode +1)) +#+end_src + +**** evil-cleverparens +:PROPERTIES: +:CUSTOM_ID: h:b06a378d-5248-4451-8eee-e65a3a768b1d +:END: + +This helps keeping parentheses balanced which is useful when writing in languages like Elisp. I do not activate this by default, as most languages do not profit from this enough in my eyes. + +#+begin_src emacs-lisp + ;; for parentheses-heavy languades modify evil commands to keep balance of parantheses + (use-package evil-cleverparens) + +#+end_src + +**** evil-surround +:PROPERTIES: +:CUSTOM_ID: h:aac82e5e-d882-4870-b644-ebdd0a2daae3 +:END: + +This minor-mode adds functionality for doing better surround-commands; for example =ci[= will let you change the word within square brackets. + +#+begin_src emacs-lisp + + ;; enables surrounding text with S + (use-package evil-surround + :config + (global-evil-surround-mode 1)) + +#+end_src + +*** ispell +:PROPERTIES: +:CUSTOM_ID: h:e888d7a7-1755-4109-af11-5358b8cf140e +:END: + +This should setup a wordlist that can be used as a dictionary. However, for some reason this does not work, and I will need to further investigate this issue. + +#+begin_src emacs-lisp + + ;; set the NixOS wordlist by hand + (setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST") + +#+end_src + +*** Font Configuration +:PROPERTIES: +:CUSTOM_ID: h:60f87342-0491-4c56-8057-6f075cf35753 +:END: + +Here I define my fonts to be used. Honestly I do not understand the face-attributes and pitches of emacs all too well. It seems this configuration works fine, but I might have to revisit this at some point in the future. + +#+begin_src emacs-lisp + + (dolist (face '(default fixed-pitch)) + (set-face-attribute face nil + :font "FiraCode Nerd Font Mono")) + (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono")) + + (set-face-attribute 'default nil :height 100) + (set-face-attribute 'fixed-pitch nil :height 1.0) + + (set-face-attribute 'variable-pitch nil + :family "IBM Plex Sans" + :weight 'regular + :height 1.06) + + ;; these settings used to be in custom.el + +#+end_src + +*** Theme +:PROPERTIES: +:CUSTOM_ID: h:72a9704b-83d2-4b74-a1f6-d333203f62db +:END: + +I have grown to love the =doom-citylights= theme and have modeled my whole system after it. Also solaire-mode is a nice mode that inverts the alt-faces with the normal faces for specific 'minor' buffers (like Help-buffers). + +#+begin_src emacs-lisp + + (use-package solaire-mode + :custom + (solaire-global-mode +1)) + + (use-package doom-themes + :hook + (server-after-make-frame . (lambda () (load-theme + 'doom-city-lights t))) + :config + (load-theme 'doom-city-lights t) + (doom-themes-treemacs-config) + (doom-themes-org-config)) + +#+end_src + +*** Icons +:PROPERTIES: +:CUSTOM_ID: h:eb0ea526-a83a-4664-b3a1-2b40d3a31493 +:END: + +This section loads the base icons used in my configuration. I am using =nerd-icons= over =all-the-icons= since the former seems to have more integrations with different packages than the latter. + +Used in: +- [[#h:b190d512-bfb5-42ec-adec-8d86bab726ce][Vertico and friends]] +- [[#h:5653d693-ecca-4c95-9633-66b9e3241070][IN USE Corfu]] + +#+begin_src emacs-lisp + + (use-package nerd-icons) + +#+end_src + +*** Variable Pitch Mode +:PROPERTIES: +:CUSTOM_ID: h:455ed7ac-ee7f-4f94-b857-f2c58b2282d0 +:END: + +This minor mode allows mixing fixed and variable pitch fonts within the same buffer. + +#+begin_src emacs-lisp + +(use-package mixed-pitch + :custom + (mixed-pitch-set-height nil) + (mixed-pitch-variable-pitch-cursor nil) + :hook + (text-mode . mixed-pitch-mode)) + + +#+end_src + +*** Modeline +:PROPERTIES: +:CUSTOM_ID: h:ed585848-875a-4673-910c-d2e1901dd95b +:END: + +Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline. Most informations I disable for it, except for the cursor information (row + column) as well as a widget for =mu4e= and git information. + +#+begin_src emacs-lisp + + (use-package doom-modeline + :init + (doom-modeline-mode) + (column-number-mode) + :custom + ((doom-modeline-height 22) + (doom-modeline-indent-info nil) + (doom-modeline-buffer-encoding nil))) + + +#+end_src + +*** Helper Modes +:PROPERTIES: +:CUSTOM_ID: h:39ae01e9-8053-4f76-aa77-8cbbbcff9652 +:END: +**** Vertico, Orderless, Marginalia, Consult, Embark +:PROPERTIES: +:CUSTOM_ID: h:b190d512-bfb5-42ec-adec-8d86bab726ce +:END: + +This set of packages uses the default emacs completion framework and works together to provide a very nice user experience: + +- Vertico simply provides a vertically stacking completion +- Marginalia adds more information to completion results +- Orderless allows for fuzzy matching +- Consult provides better implementations for several user functions, e.g. =consult-line= or =consult-outline= +- Embark allows acting on the results in the minibuffer while the completion is still ongoing - this is extremely useful since it allows to, for example, read the documentation for several functions without closing the help search. It can also collect the results of a grep operation into a seperate buffer that edits the result in their original location. + +Nerd icons is originally enabled here: [[#h:eb0ea526-a83a-4664-b3a1-2b40d3a31493][Icons]] + +***** vertico +:PROPERTIES: +:CUSTOM_ID: h:d7c7f597-f870-4e01-8f7e-27dd31dd245d +:END: + +#+begin_src emacs-lisp + + (setq read-buffer-completion-ignore-case t + read-file-name-completion-ignore-case t + completion-ignore-case t) + + (use-package vertico + :custom + (vertico-scroll-margin 0) + (vertico-count 10) + (vertico-resize t) + (vertico-cycle t) + :init + (vertico-mode) + (vertico-mouse-mode)) +#+end_src + +***** vertico-directory +:PROPERTIES: +:CUSTOM_ID: h:10d4f2bd-8c72-430b-a9ed-9b5e279ec0b4 +:END: + +This package allows for =Ido=-like directory navigation. + +#+begin_src emacs-lisp + (use-package vertico-directory + :ensure nil + :after vertico + :bind (:map vertico-map + ("RET" . vertico-directory-enter) + ("C-DEL" . vertico-directory-delete-word) + ("DEL" . vertico-directory-delete-char)) + ;; Tidy shadowed file names + :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) + +#+end_src + +***** orderless +:PROPERTIES: +:CUSTOM_ID: h:211fc0bd-0d64-4577-97d8-6abc94435f04 +:END: + +The completion styles that I chose here can possibly still be improved. I need to spend more time on this. + +#+begin_src emacs-lisp + + (use-package orderless + :custom + (completion-styles '(orderless flex basic)) + (completion-category-overrides '((file (styles . (partial-completion))) + (eglot (styles orderless))))) + +#+end_src + +***** consult +:PROPERTIES: +:CUSTOM_ID: h:49ab82bf-812d-4fbe-a5b6-d3ad703fe32c +:END: + +The big winner here are the convenient keybinds being setup here for general use. Also, I setup vim-navigation for minibuffer completions. =consult-buffer= is set twice because I am still used to that weird =C-M-j= command that I chose for =ivy-switch-buffer= when I first started using Emacs. I want to move to the other command but for now it is not feasible to delete the other one. + +#+begin_src emacs-lisp + + (use-package consult + :config + (setq consult-fontify-max-size 1024) + :bind + (("C-x b" . consult-buffer) + ("C-c " . consult-global-mark) + ("C-c C-a" . consult-org-agenda) + ("C-x O" . consult-org-heading) + ("C-M-j" . consult-buffer) + ("C-s" . consult-line) + ("M-g M-g" . consult-goto-line) + ("M-g i" . consult-imenu) + ("M-s M-s" . consult-line-multi) + :map minibuffer-local-map + ("C-j" . next-line) + ("C-k" . previous-line))) + +#+end_src +***** embark +:PROPERTIES: +:CUSTOM_ID: h:1c564ee5-ccd7-48be-b69a-d963400c4704 +:END: + +I have stripped down the embark keybinds heavily. It is very useful to me even in it's current state, but it quickly becomes overwhelming. =embark-dwim= acts on a candidate without closing the minibuffer, which is very useful. =embark-act= lets the user choose from all actions, but has an overwhelming interface. + +#+begin_src emacs-lisp + + (use-package embark + :bind + (("C-." . embark-act) + ("M-." . embark-dwim) + ("C-h B" . embark-bindings) + ("C-c c" . embark-collect)) + :custom + (prefix-help-command #'embark-prefix-help-command) + (embark-quit-after-action '((t . nil))) + :config + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +#+end_src +***** embark-consult +:PROPERTIES: +:CUSTOM_ID: h:6287551c-a6f7-4870-b3f3-210d6f038b6f +:END: + +Provides previews for embark. + +#+begin_src emacs-lisp + + (use-package embark-consult + :after (embark consult) + :demand t ; only necessary if you have the hook below + ;; if you want to have consult previews as you move around an + ;; auto-updating embark collect buffer + :hook + (embark-collect-mode . consult-preview-at-point-mode)) +#+end_src +***** marginalia +:PROPERTIES: +:CUSTOM_ID: h:f32040a4-882f-4e6b-97f1-a0105c44c034 +:END: + +I set the annotation-mode of marginalia to =heavy=. This gives even more information on the stuff that you are looking at. One thing I am missing from ivy is the highlighting on =mode=-commands based on the current state of the mode. Also, I do not understand all the shorthands used by marginalia yet. + +#+begin_src emacs-lisp + (use-package marginalia + :after vertico + :init + (marginalia-mode) + (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) + +#+end_src +***** nerd-icons-completion +:PROPERTIES: +:CUSTOM_ID: h:d70ec2fb-da43-4523-9ee4-774ececdb80e +:END: + +As stated above, this simply provides nerd-icons to the completion framework. + +#+begin_src emacs-lisp + + (use-package nerd-icons-completion + :after (marginalia nerd-icons) + :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) + :init + (nerd-icons-completion-mode)) + + +#+end_src + +**** Helpful + which-key: Better help defaults +:PROPERTIES: +:CUSTOM_ID: h:cbf6bd48-2503-489a-89da-e3359564e989 +:END: + +This pair of packages provides information on keybinds in addition to function names, which makes it easier to remember keybinds (=which-key=). The =helpful= package provides a better =Help= framework for Emacs. For some reason, the Help windows are always being focused by the cursor even though I have set =help-window-select= to nil. I do not understand why. + +#+begin_src emacs-lisp + + (use-package which-key + :init (which-key-mode) + :diminish which-key-mode + :config + (setq which-key-idle-delay 0.3)) + + (use-package helpful + :bind + (("C-h f" . helpful-callable) + ("C-h v" . helpful-variable) + ("C-h k" . helpful-key) + ("C-h C-." . helpful-at-point)) + :config + (setq help-window-select nil)) +#+end_src + +*** Ligatures +:PROPERTIES: +:CUSTOM_ID: h:bbbd9cc8-3a84-4810-a3d5-b8536a5fbda1 +:END: + +Personally, I think ligatures are fancy. With this mode, they stay 'cursorable'. However, I do not need them in all modes, so I only use them in programming modes. + +#+begin_src emacs-lisp + + (use-package ligature + :init + (global-ligature-mode t) + :config + (ligature-set-ligatures 'prog-mode + '("|||>" "<|||" "<==>" "" "---" "-<<" + "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" + "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "" "###" "#_(" "..<" + "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~=" + "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|" + "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" + ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:" + "<$" "<=" "<>" "<-" "<<" "<+" "" "++" "?:" "?=" + "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\" + "://" ";;"))) + +#+end_src + +*** Popup (popper) + Shackle Buffers +:PROPERTIES: +:CUSTOM_ID: h:e9d40e63-0e1f-47df-98f7-5427992588a4 +:END: + +The popper package allows to declare different buffers as 'popup-type', which sort of acts like a scratchpad. It can be toggled at any time using =popper-toggle= and the resulting frame can be freely customized (with =shackle=) to a certain size. It is also possible to prevent a buffer from appearing - I do this for example to the =*Warnings*= buffer, since usually I am not interested in it's output. + +=popper-echo-mode= shows all buffers that are currently stored as a popup in the echo area when a popup is opened - this is useful since you can cycle between all popup buffers. + +#+begin_src emacs-lisp + + (use-package popper + :bind (("M-[" . popper-toggle)) + :init + (setq popper-reference-buffers + '("\\*Messages\\*" + ("\\*Warnings\\*" . hide) + "Output\\*$" + "\\*Async Shell Command\\*" + "\\*Async-native-compile-log\\*" + help-mode + helpful-mode + "*Occur*" + "*scratch*" + "*julia*" + "*Python*" + ;; ("*tex-shell*" . hide) + (compilation-mode . hide))) + (popper-mode +1) + (popper-echo-mode +1)) + + (use-package shackle + :config + (setq shackle-rules '(("*Messages*" :select t :popup t :align right :size 0.3) + ("*Warnings*" :ignore t :popup t :align right :size 0.3) + ("*Occur*" :select t :popup t :align below :size 0.2) + ("*scratch*" :select t :popup t :align below :size 0.2) + ("*Python*" :select t :popup t :align below :size 0.2) + ("*tex-shell*" :ignore t :popup t :align below :size 0.2) + (helpful-mode :select t :popup t :align right :size 0.35) + (help-mode :select t :popup t :align right :size 0.4))) + (shackle-mode 1)) + +#+end_src +*** Indicate first and last line of buffer +:PROPERTIES: +:CUSTOM_ID: h:a6d23c8c-125f-4e36-af30-ff0a1e0d5a28 +:END: + +This places little angled indicators on the fringe of a window which indicate buffer boundaries. This is not super useful, but makes use of a space that I want to keep for aesthetic reasons anyways and makes it a bit more useful in the process. + +#+begin_src emacs-lisp + +(setq-default indicate-buffer-boundaries t) + +#+end_src +*** Authentication +:PROPERTIES: +:CUSTOM_ID: h:053a36bf-168f-4f63-a0c4-f0139dc6cc3b +:END: + +This defines the authentication sources used by =org-calfw= ([[#h:c760f04e-622f-4b3e-8916-53ca8cce6edc][Calendar]]) and [[#h:1a8585ed-d9f2-478f-a132-440ada1cde2c][Forge]]. + +#+begin_src emacs-lisp + + (setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") + auth-source-cache-expiry nil) ; default is 2h + +#+end_src +** Modules +:PROPERTIES: +:CUSTOM_ID: h:f2622fd3-7f14-47a8-8c21-33574fcbf14b +:END: + +This section houses all configuration bits that are related to a specific package that is not fundamental to my Emacs experience. + +At some point this will receive further sorting, but for now this is good enough. + +*** Org Mode +:PROPERTIES: +:CUSTOM_ID: h:99544398-72af-4382-b8e1-01b2221baff4 +:END: + +org-mode is probably my most-used mode in Emcas. It acts as my organizer, config management tool and calender even. + +Note that nearly all headings within the =Org-mode= heading are coded within the =use-package= setup, so be very careful about moving stuff about here. + +**** General org-mode +:PROPERTIES: +:CUSTOM_ID: h:877c9401-a354-4e44-a235-db1a90d19e00 +:END: + +This sets up the basic org-mode. I wrote a function to handle some of the initial org-mode behaviour in [[#h:06b77d28-3fd5-4554-8c7d-32c1b0ec8da5][org-mode setup. +]] +This part of the configuration mostly makes some aesthetic changes, enables neat LaTeX and points Emacs to some files that it needs for org-mode + +#+begin_src emacs-lisp + + (use-package org + ;;:diminish (org-indent-mode) + :hook (org-mode . swarsel/org-mode-setup) + :bind + (("C-" . org-fold-outer) + ("C-c s" . org-store-link)) + :config + (setq org-ellipsis " ⤵" + org-link-descriptive t + org-hide-emphasis-markers t) + (setq org-startup-folded t) + (setq org-support-shift-select t) + + ;; (setq org-agenda-start-with-log-mode t) + ;; (setq org-log-done 'time) + ;; (setq org-log-into-drawer t) + (setq org-startup-with-inline-images t) + (setq org-image-actual-width nil) + (setq org-format-latex-options '(:foreground "White" :background default :scale 2.0 :html-foreground "Black" :html-background "Transparent" :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\["))) + +#+end_src +**** org-agenda +:PROPERTIES: +:CUSTOM_ID: h:2b3b4eb6-68a1-476d-b5d1-940a21484f1d +:END: + +Here I setup a plethora of keywords, keybinds and paths to give my org-agenda more power. + +#+begin_src emacs-lisp + + (setq org-agenda-files '("/home/swarsel/Nextcloud/Org/Tasks.org" + "/home/swarsel/Nextcloud/Org/Archive.org" + "/home/swarsel/Nextcloud/Org/Anki.org" + "/home/swarsel/Calendars/leon_cal.org")) + + (setq org-refile-targets + '((swarsel-archive-org-file :maxlevel . 1) + (swarsel-anki-org-file :maxlevel . 1) + (swarsel-tasks-org-file :maxlevel . 1))) + + (setq org-todo-keywords + '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)") + (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)"))) + + + ;; Configure custom agenda views + (setq org-agenda-custom-commands + '(("d" "Dashboard" + ((agenda "" ((org-deadline-warning-days 7))) + (todo "NEXT" + ((org-agenda-overriding-header "Next Tasks"))) + (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects"))))) + + ("n" "Next Tasks" + ((todo "NEXT" + ((org-agenda-overriding-header "Next Tasks"))))) + + ("W" "Work Tasks" tags-todo "+work-email") + + + ("w" "Workflow Status" + ((todo "WAIT" + ((org-agenda-overriding-header "Waiting on External") + (org-agenda-files org-agenda-files))) + (todo "REVIEW" + ((org-agenda-overriding-header "In Review") + (org-agenda-files org-agenda-files))) + (todo "PLAN" + ((org-agenda-overriding-header "In Planning") + (org-agenda-todo-list-sublevels nil) + (org-agenda-files org-agenda-files))) + (todo "BACKLOG" + ((org-agenda-overriding-header "Project Backlog") + (org-agenda-todo-list-sublevels nil) + (org-agenda-files org-agenda-files))) + (todo "READY" + ((org-agenda-overriding-header "Ready for Work") + (org-agenda-files org-agenda-files))) + (todo "ACTIVE" + ((org-agenda-overriding-header "Active Projects") + (org-agenda-files org-agenda-files))) + (todo "COMPLETED" + ((org-agenda-overriding-header "Completed Projects") + (org-agenda-files org-agenda-files))) + (todo "CANC" + ((org-agenda-overriding-header "Cancelled Projects") + (org-agenda-files org-agenda-files))))))) + + +#+end_src +**** org capture templates +:PROPERTIES: +:CUSTOM_ID: h:23183635-3d46-4d7d-8eda-e0a085b335ef +:END: + +I wrote these capture templates to allow myself to quickly create Anki cards from within Emacs. I nearly never use this feature, but it cannot hurt to have. + +#+begin_src emacs-lisp + + (setq org-capture-templates + `( + ("a" "Anki basic" + entry + (file+headline swarsel-org-anki-filepath "Dispatch") + (function swarsel-anki-make-template-string)) + + ("A" "Anki cloze" + entry + (file+headline org-swarsel-anki-file "Dispatch") + "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: Cloze\n:ANKI_DECK: 🦁 All::01 ❤️ Various::00 ✨ Allgemein\n:END:\n** Text\n%?\n** Extra\n") + ("t" "Tasks / Projects") + ("tt" "Task" entry (file+olp swarsel-org-tasks-filepath "Inbox") + "* TODO %?\n %U\n %a\n %i" :empty-lines 1) + )) + ) +#+end_src + +**** Font Faces +:PROPERTIES: +:CUSTOM_ID: h:40528f5a-c8cd-471b-b862-4088e8e61860 +:END: + +Again, my understanding of the font-faces in Emacs is limited. This is mostly just tuned so that my org-files look acceptable. + +#+begin_src emacs-lisp + + + + ;; Set faces for heading levels + (with-eval-after-load 'org-faces (dolist (face '((org-level-1 . 1.1) + (org-level-2 . 0.9) + (org-level-3 . 0.9) + (org-level-4 . 0.9) + (org-level-5 . 0.9) + (org-level-6 . 0.9) + (org-level-7 . 0.9) + (org-level-8 . 0.9))) + (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) + + ;; Ensure that anything that should be fixed-pitch in Org files appears that way + (set-face-attribute 'org-block nil :inherit 'fixed-pitch) + (set-face-attribute 'org-table nil :inherit 'fixed-pitch) + (set-face-attribute 'org-formula nil :inherit 'fixed-pitch) + (set-face-attribute 'org-code nil :inherit '(shadow 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)) + +#+end_src + +**** org-appear +:PROPERTIES: +:CUSTOM_ID: h:62829574-a069-44b8-afb3-401a268d2747 +:END: + +This package makes emphasis-markers appear when the cursor moves over them. Very useful as I enjoy the clean look of not always seeing them, but it is annoying not to be able to edit them properly. + +#+begin_src emacs-lisp + + (use-package org-appear + :hook (org-mode . org-appear-mode) + :init + (setq org-appear-autolinks t) + (setq org-appear-autokeywords t) + (setq org-appear-autoentities t) + (setq org-appear-autosubmarkers t)) + +#+end_src + +**** Centered org-mode Buffers +:PROPERTIES: +:CUSTOM_ID: h:bbcfa895-4d46-4b1d-b84e-f634e982c46e +:END: + +I like org-mode buffers to be centered, as I do not find that enormous lines are of big use. + +Function definition in: [[#h:fa710375-2efe-49b4-af6a-a875aca6e4a2][Visual-fill column]] + +#+begin_src emacs-lisp + + (use-package visual-fill-column + :hook (org-mode . swarsel/org-mode-visual-fill)) + +#+end_src + +**** Fix headings not folding sometimes +:PROPERTIES: +:CUSTOM_ID: h:c1a0adea-ca97-43d7-b5a0-b856d2ebc9a8 +:END: + +There is a weird bug in org-mode that makes it so that headings were not folding correctly sometimes. This setting seems to fix it. + +#+begin_src emacs-lisp + + (setq org-fold-core-style 'overlays) + +#+end_src + +**** Babel +:PROPERTIES: +:CUSTOM_ID: h:3e0b6da3-0497-4080-bb49-bab949c03bc4 +:END: + +org-babel allows to run blocks in other programming languages within an org-mode buffer, similar to what e.g. jupyterhub offers for python. + +It also offers a very useful utility of exporting org-mode buffers to different formats; the feature I enjoy most is what makes this file useful: the tangling functionality. + +***** Language Configuration +:PROPERTIES: +:CUSTOM_ID: h:5d5ed7be-ec5f-4e17-bbb8-820ab6a9961c +:END: + +- This configures the languages that babel recognizes. + +#+begin_src emacs-lisp + + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (python . t) + (js . t) + (shell . t) + )) + + (push '("conf-unix" . conf-unix) org-src-lang-modes) + +#+end_src + +***** old easy structure templates +:PROPERTIES: +:CUSTOM_ID: h:d112ed66-b2dd-45cc-8d70-9cf6631f28a9 +:END: + +- org 9.2 changed the way structure templates work. This brings back the old way it worked. + + Usage: Type =<=, followed by one of the below keywords and press =RET=. The corresponding source block should appear. + + #+begin_src emacs-lisp + + (require 'org-tempo) + (add-to-list 'org-structure-template-alist '("sh" . "src shell")) + (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) + (add-to-list 'org-structure-template-alist '("py" . "src python :results output")) + (add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle")) + + #+end_src + +**** aucTex +:PROPERTIES: +:CUSTOM_ID: h:4696e2fc-3296-47dc-8fc3-66912c329d4c +:END: + +This provides several utilities for LaTeX in Emacs, including many completions and convenience functions for math-mode. + +#+begin_src emacs-lisp + + (use-package auctex) + (setq TeX-auto-save t) + (setq TeX-save-query nil) + (setq TeX-parse-self t) + (setq-default TeX-master nil) + + (add-hook 'LaTeX-mode-hook 'visual-line-mode) + (add-hook 'LaTeX-mode-hook 'flyspell-mode) + (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode) + (add-hook 'LaTeX-mode-hook 'reftex-mode) + (setq LaTeX-electric-left-right-brace t) + (setq font-latex-fontify-script nil) + (setq TeX-electric-sub-and-superscript t) + ;; (setq reftex-plug-into-AUCTeX t) + +#+end_src + +**** org-download +:PROPERTIES: +:CUSTOM_ID: h:406e5ecb-66f0-49bf-85ca-8b499f73ec5b +:END: + +This package allows to download and copy images into org-mode buffers. Sadly it does not work in a very stable manner - if you copy images that are also links to another page (like is often the case in a Google image search), Emacs might crash from this. + +#+begin_src emacs-lisp + + (use-package org-download + :after org + :defer nil + :custom + (org-download-method 'directory) + (org-download-image-dir "./images") + (org-download-heading-lvl 0) + (org-download-timestamp "org_%Y%m%d-%H%M%S_") + ;;(org-image-actual-width 500) + (org-download-screenshot-method "grim -g \"$(slurp)\" %s") + :bind + ("C-M-y" . org-download-screenshot) + :config + (require 'org-download)) + +#+end_src + +**** org-fragtog +:PROPERTIES: +:CUSTOM_ID: h:a02b1162-3e19-46f1-8efc-9f375971645c +:END: + +This package automatically toggles LaTeX-fragments in org-files. It seems to also work in markdown-files which is a nice addition, as my Obsidian notes are held in markdown. + +#+begin_src emacs-lisp + + (use-package org-fragtog) + (add-hook 'org-mode-hook 'org-fragtog-mode) + (add-hook 'markdown-mode-hook 'org-fragtog-mode) + +#+end_src + +**** org-modern +:PROPERTIES: +:CUSTOM_ID: h:95b42e77-767c-4461-9ba8-b1c1cd18266c +:END: + +This just makes org-mode a little bit more beautiful, mostly by making the =begin_src= and =end_src= tags in source-blocks turn into more beautiful icons, as well as hiding =#+= tags before them, as well as in the properties section of the file. + +#+begin_src emacs-lisp + + (use-package org-modern + :config (setq org-modern-block-name + '((t . t) + ("src" "»" "∥"))) + :hook (org-mode . org-modern-mode)) + +#+end_src + +**** Presentations +:PROPERTIES: +:CUSTOM_ID: h:4e11a845-a7bb-4eb5-b4ce-5b2f52e07425 +:END: + +Recently I have grown fond of holding presentations using Emacs :) + +#+begin_src emacs-lisp + + (use-package org-present + :bind (:map org-present-mode-keymap + ("q" . org-present-quit) + ("" . swarsel/org-present-prev) + ("" . 'ignore) + ("" . 'ignore) + ("" . swarsel/org-present-next)) + :hook ((org-present-mode . swarsel/org-present-start) + (org-present-mode-quit . swarsel/org-present-end)) + ) + + + (use-package hide-mode-line) + + (defun swarsel/org-present-start () + (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-verbatim (:height 1.55) org-verbatim) + (org-block (:height 1.25) org-block) + (org-block-begin-line (:height 0.7) org-block) + )) + (dolist (face '((org-level-1 . 1.1) + (org-level-2 . 1.2) + (org-level-3 . 1.2) + (org-level-4 . 1.2) + (org-level-5 . 1.2) + (org-level-6 . 1.2) + (org-level-7 . 1.2) + (org-level-8 . 1.2))) + (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) + + (setq header-line-format " ") + (setq visual-fill-column-width 90) + (setq indicate-buffer-boundaries nil) + (setq inhibit-message nil) + (breadcrumb-mode 0) + (org-display-inline-images) + (global-hl-line-mode 0) + (display-line-numbers-mode 0) + (org-modern-mode 0) + (evil-insert-state 1) + (beginning-of-buffer) + (org-present-read-only) + ;; (org-present-hide-cursor) + (swarsel/org-present-slide) + ) + + (defun swarsel/org-present-end () + (setq-local face-remapping-alist '((default variable-pitch default))) + (dolist (face '((org-level-1 . 1.1) + (org-level-2 . 0.9) + (org-level-3 . 0.9) + (org-level-4 . 0.9) + (org-level-5 . 0.9) + (org-level-6 . 0.9) + (org-level-7 . 0.9) + (org-level-8 . 0.9))) + (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face))) + (setq header-line-format nil) + (setq visual-fill-column-width 150) + (setq indicate-buffer-boundaries t) + (setq inhibit-message nil) + (breadcrumb-mode 1) + (global-hl-line-mode 1) + (display-line-numbers-mode 1) + (org-remove-inline-images) + (org-modern-mode 1) + (evil-normal-state 1) + ;; (org-present-show-cursor) + ) + + (defun swarsel/org-present-slide () + (org-overview) + (org-show-entry) + (org-show-children) + ) + + (defun swarsel/org-present-prev () + (interactive) + (org-present-prev) + (swarsel/org-present-slide)) + + (defun swarsel/org-present-next () + (interactive) + (unless (eobp) + (org-next-visible-heading 1) + (org-fold-show-entry)) + (when (eobp) + (org-present-next) + (swarsel/org-present-slide) + )) + + (defun clojure-leave-clojure-mode-function () + ) + + (add-hook 'buffer-list-update-hook #'clojure-leave-clojure-mode-function) + (add-hook 'org-present-mode-hook 'swarsel/org-present-start) + (add-hook 'org-present-mode-quit-hook 'swarsel/org-present-end) + (add-hook 'org-present-after-navigate-functions 'swarsel/org-present-slide) + +#+end_src +*** Nix Mode +:PROPERTIES: +:CUSTOM_ID: h:406c2ecc-0e3e-4d9f-9ae3-3eb1f8b87d1b +:END: + +This adds a rudimentary nix-mode to Emacs. I have not really tried this out, as I am mostly editing nix-files in org-mode anyways. + +#+begin_src emacs-lisp + + (use-package nix-mode + :mode "\\.nix\\'") + +#+end_src + +*** Markdown Mode +:PROPERTIES: +:CUSTOM_ID: h:50327461-a11b-4e81-830a-90febc720cfa +:END: +**** Mode +:PROPERTIES: +:CUSTOM_ID: h:734dc40a-a2c4-4839-b884-cb99b81aa6fe +:END: + +#+begin_src emacs-lisp + + (setq markdown-command "pandoc") + + (use-package markdown-mode + :ensure t + :mode ("README\\.md\\'" . gfm-mode) + :init (setq markdown-command "multimarkdown") + :bind (:map markdown-mode-map + ("C-c C-e" . markdown-do))) + +#+end_src + +**** LaTeX in Markdown +:PROPERTIES: +:CUSTOM_ID: h:8d90fe51-0b32-423a-a159-4f853bc29b68 +:END: + +#+begin_src emacs-lisp + + (add-hook 'markdown-mode-hook + (lambda () + (local-set-key (kbd "C-c C-x C-l") 'org-latex-preview) + (local-set-key (kbd "C-c C-x C-u") 'markdown-toggle-url-hiding) + )) + +#+end_src + +*** Olivetti +:PROPERTIES: +:CUSTOM_ID: h:65e69741-9860-4ed0-bbed-7b7be9a2a9d6 +:END: + +Olivetti is a mode specialized for writing prose in Emacs. I went for a very simple setup with little distractions. + +This mode is not automatically activated anywhere because I only rarely need it. + +#+begin_src emacs-lisp + + (use-package olivetti + :init + (setq olivetti-body-width 100) + (setq olivetti-recall-visual-line-mode-entry-state t)) + +#+end_src + +*** darkroom +:PROPERTIES: +:CUSTOM_ID: h:94d4a0dc-b0d7-4702-b760-beeaa6da2b8f +:END: + +Darkroom is package that reduces all forms of distraction to a minimum - this can be useful when simply reading a file for example. For this mode I have increased the text scale by a large margin to make for comfortable reading +This mode is not automatically activated anywhere because I only rarely need it. + +#+begin_src emacs-lisp + +(use-package darkroom + :init + (setq darkroom-text-scale-increase 3)) + +#+end_src + +*** Ripgrep +:PROPERTIES: +:CUSTOM_ID: h:87453f1c-8ea5-4d0a-862d-8973d5bc5405 +:END: + +This is the ripgrep command for Emacs. + +#+begin_src emacs-lisp + + (use-package rg) + +#+end_src +*** Tree-sitter +:PROPERTIES: +:CUSTOM_ID: h:543641d0-02a9-459e-a2d6-96c8fcc06864 +:END: + +Tree-sitter is a parsing library integrated into Emacs to provide better syntax highlighting and code analysis. It generates concrete syntax trees for source code, enabling more accurate and efficient text processing. Emacs' tree-sitter integration enhances language support, offering features like incremental parsing and precise syntax-aware editing. This improves the development experience by providing robust and dynamic syntax features, making it easier for me to navigate and manipulate code. + +In order to update the language grammars, run the next command below. + +#+begin_src emacs-lisp :tangle no :export both + + (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist)) + +#+end_src + +#+RESULTS: +| bash | c | cmake | cpp | css | elisp | go | html | javascript | json | julia | latex | make | markdown | R | python | typescript | rust | sql | toml | tsx | yaml | + +#+begin_src emacs-lisp + + (use-package emacs + :ensure nil + :init + (setq treesit-language-source-alist + '((bash . ("https://github.com/tree-sitter/tree-sitter-bash")) + (c . ("https://github.com/tree-sitter/tree-sitter-c")) + (cmake . ("https://github.com/uyha/tree-sitter-cmake")) + (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp")) + (css . ("https://github.com/tree-sitter/tree-sitter-css")) + (elisp . ("https://github.com/Wilfred/tree-sitter-elisp")) + (go . ("https://github.com/tree-sitter/tree-sitter-go")) + (html . ("https://github.com/tree-sitter/tree-sitter-html")) + (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript")) + (json . ("https://github.com/tree-sitter/tree-sitter-json")) + (julia . ("https://github.com/tree-sitter/tree-sitter-julia")) + (latex . ("https://github.com/latex-lsp/tree-sitter-latex")) + (make . ("https://github.com/alemuller/tree-sitter-make")) + (markdown . ("https://github.com/ikatyang/tree-sitter-markdown")) + (R . ("https://github.com/r-lib/tree-sitter-r")) + (python . ("https://github.com/tree-sitter/tree-sitter-python")) + (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "typescript/src" "typescript")) + (rust . ("https://github.com/tree-sitter/tree-sitter-rust")) + (sql . ("https://github.com/m-novikov/tree-sitter-sql")) + (toml . ("https://github.com/tree-sitter/tree-sitter-toml")) + (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")) + (yaml . ("https://github.com/ikatyang/tree-sitter-yaml")))) + ) + + (use-package treesit-auto + :config + (global-treesit-auto-mode) + (setq treesit-auto-install 'prompt)) + + +#+end_src + +*** direnv +:PROPERTIES: +:CUSTOM_ID: h:82ddeef2-99f8-465b-ba36-07c3eaad717b +:END: + +#+begin_src emacs-lisp + + (use-package direnv + :custom (direnv-always-show-summary nil) + :config (direnv-mode)) + +#+end_src + +*** avy +:PROPERTIES: +:CUSTOM_ID: h:efb3f0fd-e846-4df9-ba48-2e45d776f68f +:END: + +=avy= provides the ability to search for any character on the screen (not only in the current buffer!) - I enjoy this utility a lot and use it possibly even more often than the native vim commands. + +#+begin_src emacs-lisp + + (use-package avy + :bind + (("M-o" . avy-goto-char-timer)) + :config + (setq avy-all-windows 'all-frames)) + +#+end_src + +*** crdt (Collaborative Editing) +:PROPERTIES: +:CUSTOM_ID: h:1c1821c6-98de-4079-a4f3-6ba6e6dcb668 +:END: + +With this it is possible to work on the same file collaboratively. I have never tried it out, but it sounds cool. + +#+begin_src emacs-lisp + +(use-package crdt) + +#+end_src + +*** devdocs +:PROPERTIES: +:CUSTOM_ID: h:d9a6cb44-736e-4608-951f-e928e1b757c0 +:END: + +=devdocs= is a very nice package that provides documentation from [[https:devdocs.io]]. This is very useful since e.g. =pyright= provides only a very bad documentation and I do not want to leave Emacs all the time just to read documentation. + +To install a documentation, use the =devdocs=install= command and select the appropriate version. =devdocs-update-all= can be used to download and reinstall all installed documents if a newer version is available. Check documentation with =devdocs-lookup= (=C-SPC h d=). + +#+begin_src emacs-lisp + + (use-package devdocs) + + (add-hook 'python-mode-hook + (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1")))) + (add-hook 'python-ts-mode-hook + (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1")))) + + (add-hook 'c-mode-hook + (lambda () (setq-local devdocs-current-docs '("c")))) + (add-hook 'c-ts-mode-hook + (lambda () (setq-local devdocs-current-docs '("c")))) + + (add-hook 'c++-mode-hook + (lambda () (setq-local devdocs-current-docs '("cpp")))) + (add-hook 'c++-ts-mode-hook + (lambda () (setq-local devdocs-current-docs '("cpp")))) + + (devdocs-update-all) + +#+end_src + +*** Projectile +:PROPERTIES: +:CUSTOM_ID: h:5cde5032-251e-4cc4-9202-b4ce996f92c2 +:END: + +projectile is useful for keeping track of your git projects within Emacs. I mostly use it to quickly switch between projects. + +#+begin_src emacs-lisp + + (use-package projectile + :diminish projectile-mode + :config (projectile-mode) + :custom ((projectile-completion-system 'auto)) ;; integrate ivy into completion system + :bind-keymap + ("C-c p" . projectile-command-map) ; all projectile commands under this + :init + ;; NOTE: Set this to the folder where you keep your Git repos! + (when (file-directory-p swarsel-projects-directory) + (setq projectile-project-search-path (list swarsel-projects-directory))) + (setq projectile-switch-project-action #'magit-status)) + +#+end_src + +*** Magit +:PROPERTIES: +:CUSTOM_ID: h:d2c7323d-f8c6-4f23-b70a-930e3e4ecce5 +:END: + +magit is the best git utility I have ever used - it has a beautiful interface and is very verbose. Here I mostly just setup the list of repositories that I want to expost to magit. + +Also, Emacs needs a little extra love to accept my Yubikey for git commits etc. We also set that here. + +#+begin_src emacs-lisp + + (use-package magit + :config + (setq magit-repository-directories `((,swarsel-projects-directory . 1) + (,swarsel-emacs-directory . 0) + (,swarsel-obsidian-directory . 0) + ("~/.dotfiles/" . 0))) + :custom + (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ; stay in the same window + + ;; yubikey support for pushing commits + ;; commiting is enabled through nixos gpg-agent config + (setq epg-pinentry-mode 'loopback) + (setenv "SSH_AUTH_SOCK" (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket"))) +#+end_src + +*** Forge +:PROPERTIES: +:CUSTOM_ID: h:1a8585ed-d9f2-478f-a132-440ada1cde2c +:END: + +NOTE: Make sure to configure a GitHub token before using this package! +- https://magit.vc/manual/forge/Token-Creation.html#Token-Creation +- https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started +- https://magit.vc/manual/ghub/Storing-a-Token.html +- https://www.emacswiki.org/emacs/GnuPG + + (1) in practice: github -<> settings -<> developer option -<> + create classic token with repo; user; read:org permissions + (2) install GnuGP (and add to PATH) + (3) create ~/.authinfo.gpg with the following info scheme: + machine api.github.com login USERNAME^forge password 012345abcdef... + +#+begin_src emacs-lisp + + (use-package forge + :after magit) + + (with-eval-after-load 'forge + (add-to-list 'forge-alist + '("sgit.iue.tuwien.ac.at" + "sgit.iue.tuwien.ac.at/api/v1" + "sgit.iue.tuwien.ac.at" + forge-gitea-repository))) +#+end_src + +*** git-timemachine +:PROPERTIES: +:CUSTOM_ID: h:cf5b0e6b-56a5-4a93-99fb-258eb7cb2eb4 +:END: + +This is just a nice utility to browse different versions of a file of a git project within Emacs. + +#+begin_src emacs-lisp + + (use-package git-timemachine + :hook (git-time-machine-mode . evil-normalize-keymaps) + :init (setq git-timemachine-show-minibuffer-details t)) + +#+end_src + +*** Delimiters (brackets): rainbow-delimiters, highlight-parentheses +:PROPERTIES: +:CUSTOM_ID: h:d9671ab7-a75a-47c6-a1f4-376d126c9b0a +:END: + +- rainbow-delimiters colors all delimiters, also ones not in current selection +- paren highlights the current delimiter selection especially bold +- highlight-parentheses boldly highlights all delimiters in current selection + +I am not completely sure on electric-pair-mode yet, sometimes it is very helpful, sometimes it annoys me to no end. + +#+begin_src emacs-lisp + + (use-package rainbow-delimiters + :hook (prog-mode . rainbow-delimiters-mode)) + + (use-package highlight-parentheses + :config + (setq highlight-parentheses-colors '("black" "white" "black" "black" "black" "black" "black")) + (setq highlight-parentheses-background-colors '("magenta" "blue" "cyan" "green" "yellow" "orange" "red")) + (global-highlight-parentheses-mode t)) + + (electric-pair-mode 1) + (setq electric-pair-preserve-balance nil) + ;; don't try to be overly smart + (setq electric-pair-delete-adjacent-pairs nil) + ;; don't skip newline when auto-pairing parenthesis + (setq electric-pair-skip-whitespace-chars '(9 32)) + + ;; in org-mode buffers, do not pair < and > in order not to interfere with org-tempo + (add-hook 'org-mode-hook (lambda () + (setq-local electric-pair-inhibit-predicate + `(lambda (c) + (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c)))))) + +#+end_src + +*** rainbow-mode +:PROPERTIES: +:CUSTOM_ID: h:d1a32a69-2f9a-45ef-95fe-a00e3551dc94 +:END: + +Complimentary to the delimiters-packages above, this package sets the background color of the delimiters, which makes it easier to see at a glance where we are in a delimiter-tree. + +#+begin_src emacs-lisp + + (use-package rainbow-mode + :config (rainbow-mode)) + +#+end_src +*** Corfu +:PROPERTIES: +:CUSTOM_ID: h:5653d693-ecca-4c95-9633-66b9e3241070 +:END: + +This is the company equivalent to the vertico gang. +I dislike the standard behaviour that makes the cursor move into the completion framework on presses of == and ==. + +Nerd icons is originally enabled here: [[#h:eb0ea526-a83a-4664-b3a1-2b40d3a31493][Icons]] + +Navigation functions defined here: [[#h:a1802f9b-bb71-4fd5-86fa-945da18e8b81][corfu: Do not interrupt navigation]] + +#+begin_src emacs-lisp + + ;; (use-package corfu + ;; :custom + ;; (corfu-cycle t) + ;; :init + ;; (global-corfu-mode)) + + (use-package corfu + :init + (global-corfu-mode) + (corfu-history-mode) + (corfu-popupinfo-mode) ; Popup completion info + :custom + (corfu-auto t) + (corfu-auto-prefix 3) + (corfu-auto-delay 0.3) + (corfu-cycle t) + (corfu-quit-no-match 'separator) + (corfu-separator ?\s) + ;; (corfu-quit-no-match t) + (corfu-popupinfo-max-height 70) + (corfu-popupinfo-delay '(0.5 . 0.2)) + ;; (corfu-preview-current 'insert) ; insert previewed candidate + (corfu-preselect 'prompt) + (corfu-on-exact-match nil) ; Don't auto expand tempel snippets + ;; Optionally use TAB for cycling, default is `corfu-complete'. + :bind (:map corfu-map + ("M-SPC" . corfu-insert-separator) + ("" . swarsel/corfu-normal-return) + ;; ("C-" . swarsel/corfu-complete) + ("S-" . corfu-popupinfo-scroll-down) + ("S-" . corfu-popupinfo-scroll-up) + ("C-" . corfu-previous) + ("C-" . corfu-next) + (" " . swarsel/corfu-quit-and-up) + (" " . swarsel/corfu-quit-and-down)) + ) + + (use-package nerd-icons-corfu) + + (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter) + + (setq nerd-icons-corfu-mapping + '((array :style "cod" :icon "symbol_array" :face font-lock-type-face) + (boolean :style "cod" :icon "symbol_boolean" :face font-lock-builtin-face) + ;; ... + (t :style "cod" :icon "code" :face font-lock-warning-face))) + +#+end_src + +*** cape +:PROPERTIES: +:CUSTOM_ID: h:c3cc1c12-3ab8-42b7-be07-63f54eac397f +:END: + +cape adds even more completion capabilities by adding a lot of completion logic that is exposed as separate functions. I tried out adding these to the =completion-at-points-functions= alist, but I felt like it cluttered my suggestions too much. Hence I now just call the respective functions when I need them. For this I setup the =C-z= keybinding in [[#h:218376e8-086b-46bf-91b3-78295d5d440f][General evil]]. + +I leave the commented out alist extensions here in case I want to try them out at some point in the future. + +#+begin_src emacs-lisp + + (use-package cape + :bind + ("C-z p" . completion-at-point) ;; capf + ("C-z t" . complete-tag) ;; etags + ("C-z d" . cape-dabbrev) ;; or dabbrev-completion + ("C-z h" . cape-history) + ("C-z f" . cape-file) + ("C-z k" . cape-keyword) + ("C-z s" . cape-elisp-symbol) + ("C-z e" . cape-elisp-block) + ("C-z a" . cape-abbrev) + ("C-z l" . cape-line) + ("C-z w" . cape-dict) + ("C-z :" . cape-emoji) + ("C-z \\" . cape-tex) + ("C-z _" . cape-tex) + ("C-z ^" . cape-tex) + ("C-z &" . cape-sgml) + ("C-z r" . cape-rfc1345) + ;; Add to the global default value of `completion-at-point-functions' which is + ;; used by `completion-at-point'. The order of the functions matters, the + ;; first function returning a result wins. Note that the list of buffer-local + ;; completion functions takes precedence over the global list. + ;; (add-to-list 'completion-at-point-functions #'cape-dabbrev) + ;; (add-to-list 'completion-at-point-functions #'cape-file) + ;; (add-to-list 'completion-at-point-functions #'cape-elisp-block) + ;; (add-to-list 'completion-at-point-functions #'cape-history) + ;; (add-to-list 'completion-at-point-functions #'cape-keyword) + ;; (add-to-list 'completion-at-point-functions #'cape-tex) + ;; (add-to-list 'completion-at-point-functions #'cape-sgml) + ;; (add-to-list 'completion-at-point-functions #'cape-rfc1345) + ;; (add-to-list 'completion-at-point-functions #'cape-abbrev) + ;; (add-to-list 'completion-at-point-functions #'cape-dict) + ;; (add-to-list 'completion-at-point-functions #'cape-elisp-symbol) + ;; (add-to-list 'completion-at-point-functions #'cape-line) + ) + +#+end_src + +*** rust +:PROPERTIES: +:CUSTOM_ID: h:3aa20438-edf6-4b13-a90d-3d5c51239c44 +:END: + +This is supposed to setup a rust-lsp - however, this has not worked nicely in the past and this configuration section is just a ruin really. I need to check what works and clean this up. + +#+begin_src emacs-lisp + + ;; (use-package rustic + ;; :ensure + ;; :bind (:map rustic-mode-map + ;; ("M-j" . lsp-ui-imenu) + ;; ("M-?" . lsp-find-references) + ;; ("C-c C-c l" . flycheck-list-errors) + ;; ("C-c C-c a" . lsp-execute-code-action) + ;; ("C-c C-c r" . lsp-rename) + ;; ("C-c C-c q" . lsp-workspace-restart) + ;; ("C-c C-c Q" . lsp-workspace-shutdown) + ;; ("C-c C-c s" . lsp-rust-analyzer-status)) + ;; :config + + ;; (setq rustic-format-on-save t) + ;; (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook)) + + ;; (defun rk/rustic-mode-hook () + ;; ;; so that run C-c C-c C-r works without having to confirm, but don't try to + ;; ;; save rust buffers that are not file visiting. Once + ;; ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should + ;; ;; no longer be necessary. + ;; (when buffer-file-name + ;; (setq-local buffer-save-without-query t)) + ;; (add-hook 'before-save-hook 'lsp-format-buffer nil t)) + + ;; (use-package rustic + ;; :config + ;; (setq rustic-format-on-save t) + ;; (setq rustic-lsp-client 'eglot) + ;; :custom + ;; (lsp-rust-analyzer-cargo-watch-command "clippy") + ;; (lsp-rust-analyzer-server-display-inlay-hints t) + ;; :mode ("\\.rs" . rustic-mode)) + + (use-package rustic + :config + (setq rustic-format-on-save t) + (setq rustic-lsp-client 'eglot) + :mode ("\\.rs" . rustic-mode)) +#+end_src + +*** Tramp +:PROPERTIES: +:CUSTOM_ID: h:b9b27a88-06f3-470b-a604-a20b2079bc26 +:END: + +Tramp allows for SSH access of files over Emacs. I have no ideas what the options here mean, but this is a recommended configuration that I found (sadly I lost the link). I need to research more what these options really do. + +#+begin_src emacs-lisp + + + +(use-package tramp + :init + (setq vc-ignore-dir-regexp + (format "\\(%s\\)\\|\\(%s\\)" + vc-ignore-dir-regexp + tramp-file-name-regexp)) + (setq tramp-default-method "ssh") + (setq tramp-auto-save-directory + (expand-file-name "tramp-auto-save" user-emacs-directory)) + (setq tramp-persistency-file-name + (expand-file-name "tramp-connection-history" user-emacs-directory)) + (setq password-cache-expiry nil) + (setq tramp-use-ssh-controlmaster-options nil) + (setq remote-file-name-inhibit-cache nil) + :config + (customize-set-variable 'tramp-ssh-controlmaster-options + (concat + "-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p " + "-o ControlMaster=auto -o ControlPersist=yes")) +) + + + +#+end_src + +*** diff-hl +:PROPERTIES: +:CUSTOM_ID: h:58415e95-8a7a-4517-acbb-5f1bb1028603 +:END: + +This is a simple highlighting utility that uses the margin to visually show the differences since the last git commit. + +#+begin_src emacs-lisp + + (use-package diff-hl + :hook + ((prog-mode + org-mode) . diff-hl-mode) + :init + (diff-hl-flydiff-mode) + (diff-hl-margin-mode) + (diff-hl-show-hunk-mouse-mode)) + +#+end_src +*** Commenting +:PROPERTIES: +:CUSTOM_ID: h:d60ce0b1-cabf-43f5-a236-a1e4b400d2f5 +:END: + +This package allows for swift commenting out and in of code snippets. For some reason, it is a bit broken in my config, as it sometimes comments out too much, sometimes too little, and sometimes it splits lines during commenting. Also, in org-mode when inside a src-block, it often times jumps to the top of the block. + +Still, this is avery convenient package. + +#+begin_src emacs-lisp + + (use-package evil-nerd-commenter + :bind ("M-/" . evilnc-comment-or-uncomment-lines)) + +#+end_src + +*** yasnippet +:PROPERTIES: +:CUSTOM_ID: h:9ec11ee4-2250-414a-87b5-73ee680a3a4a +:END: + +yasnippet allows to define snippets that can be quickly expanded by hitting the =TAB= key after inputting a keyword. + +I used to run this together with the =yasnippet-snippets= package, but the snippets in there I did not find all too useful for myself. I need to create some custom snippets here one day. + +#+begin_src emacs-lisp + + (use-package yasnippet + :init (yas-global-mode 1) + :config + (yas-reload-all)) + +#+end_src + +***** yasnippet math-snippets +:PROPERTIES: +:CUSTOM_ID: h:af0a78a5-17c2-4e13-b64a-772c27c4dee2 +:END: + +The following block is mostly inspired from [[https://code.kulupu.party/thesuess/WTFmacs/]] and sets up a few prefixes that make LaTeX-math-mode nicer to use even with auctex and cape enabled. + +#+begin_src emacs-lisp + + + (setq wtf/latex-mathbb-prefix "''") + (setq swarsel/latex-mathcal-prefix "``") + + (use-package yasnippet + :config + + (setq wtf/english-alphabet + '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z")) + + (dolist (elem wtf/english-alphabet) + (when (string-equal elem (downcase elem)) + (add-to-list 'wtf/english-alphabet (upcase elem)))) + + + (yas-define-snippets + 'latex-mode + (mapcar + (lambda (elem) + (list (concat wtf/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem))) + wtf/english-alphabet)) + + (yas-define-snippets + 'latex-mode + (mapcar + (lambda (elem) + (list (concat swarsel/latex-mathcal-prefix elem) (concat "\\mathcal{" elem "}") (concat "Mathcal letter " elem))) + wtf/english-alphabet)) + + (setq swtf/latex-math-symbols + '(("x" . "\\times") + ("*" . "\\cdot") + ("." . "\\ldots") + ("op" . "\\operatorname{$1}$0") + ("o" . "\\circ") + ("V" . "\\forall") + ("v" . "\\vee") + ("w" . "\\wedge") + ("q" . "\\quad") + ("f" . "\\frac{$1}{$2}$0") + ("s" . "\\sum_{$1}^{$2}$0") + ("p" . "\\prod_{$1}^{$2}$0") + ("e" . "\\exists") + ("i" . "\\int_{$1}^{$2}$0") + ("c" . "\\cap") + ("u" . "\\cup") + ("0" . "\\emptyset"))) + + ) + + +#+end_src + +*** eglot +:PROPERTIES: +:CUSTOM_ID: h:316857e7-4df8-4ec5-b22e-6dac918fa937 +:END: + +After having tried out =lsp-mode= and =lsp-bridge= for a while each, I must say that =eglot= feels the most clean and fast to me. Rust-modes need a little extra care to get working here. + +:PROPERTIES: +:CUSTOM_ID: h:424fbc62-84e2-42c7-a1ca-e43ea04c43e5 +:END: + +#+begin_src emacs-lisp + + (use-package eglot + :ensure nil + :hook + ((python-mode + python-ts-mode + c-mode + c-ts-mode + c++-mode + c++-ts-mode + tex-mode + LaTeX-mode + ) . (lambda () (progn + (eglot-ensure) + (add-hook 'before-save-hook 'eglot-format nil 'local)))) + :custom + (eldoc-echo-area-use-multiline-p nil) + (completion-category-defaults nil) + :config + ;; (push '(rustic-ts-mode . eglot-rust-analyzer) eglot-server-programs) + (push '(rustic-mode . eglot-rust-analyzer) eglot-server-programs) + (add-to-list 'eglot-server-programs '((rust-mode) . (eglot-rust-analyzer "rust-analyzer"))) + ;; (add-to-list 'eglot-server-programs '((python-mode) . ("pylsp"))) + ;; (add-to-list 'eglot-server-programs '((c-mode) . ("clangd"))) + :bind (:map eglot-mode-map + ("M-(" . flymake-goto-next-error) + ("C-c ," . eglot-code-actions))) + + (defalias 'start-lsp-server #'eglot) + +#+end_src + +*** Breadcrumb +:PROPERTIES: +:CUSTOM_ID: h:1de35f27-335d-4cbd-beb6-f85cf5496173 +:END: + +This simple shows the path to the current file on the top of the buffer - I just think it looks kind of neat, even though it is not extremely useful :) + +#+begin_src emacs-lisp + + (use-package breadcrumb + :config (breadcrumb-mode)) + +#+end_src + +*** Prevent breaking of hardlinks +:PROPERTIES: +:CUSTOM_ID: h:e9a30d0f-423f-4e85-af4b-f8560f1c1b53 +:END: + +This setting ensures that hard links are preserved during the backup process, which is useful for maintaining the integrity of files that are linked in multiple locations. + +#+begin_src emacs-lisp + + (setq backup-by-copying-when-linked t) + +#+end_src + +*** Dirvish +:PROPERTIES: +:CUSTOM_ID: h:0918557a-8463-430c-b8df-6546dea9abd0 +:END: + +Dirvish is an improvement upon the dired-framework and has more features like file preview etc. Sadly it has an incompatibility with =openwith= which is why I have disabled that package. + +#+begin_src emacs-lisp + + (use-package dirvish + :init + (dirvish-override-dired-mode) + :config + (dirvish-peek-mode) + (dirvish-side-follow-mode) + (setq dirvish-open-with-programs + (append dirvish-open-with-programs '( + (("xlsx" "docx" "doc" "odt" "ods") "libreoffice" "%f") + (("jpg" "jpeg" "png") "imv" "%f") + (("pdf") "sioyek" "%f") + (("xopp") "xournalpp" "%f")))) + :custom + (delete-by-moving-to-trash t) + (dired-listing-switches + "-l --almost-all --human-readable --group-directories-first --no-group") + (dirvish-attributes + '(vc-state subtree-state nerd-icons collapse file-time file-size)) + (dirvish-quick-access-entries + '(("h" "~/" "Home") + ("c" "~/.dotfiles/" "Config") + ("d" "~/Downloads/" "Downloads") + ("D" "~/Documents/" "Documents") + ("p" "~/Documents/GitHub/" "Projects") + ("/" "/" "Root"))) + :bind + ((" d" . 'dirvish) + ("C-=" . 'dirvish-side) + :map dirvish-mode-map + ("h" . dired-up-directory) + ("" . dired-up-directory) + ("l" . dired-find-file) + ("" . dired-find-file) + ("j" . evil-next-visual-line) + ("k" . evil-previous-visual-line) + ("a" . dirvish-quick-access) + ("f" . dirvish-file-info-menu) + ("z" . dirvish-history-last) + ("J" . dirvish-history-jump) + ("y" . dirvish-yank-menu) + ("/" . dirvish-narrow) + ("TAB" . dirvish-subtree-toggle) + ("M-f" . dirvish-history-go-forward) + ("M-b" . dirvish-history-go-backward) + ("M-l" . dirvish-ls-switches-menu) + ("M-m" . dirvish-mark-menu) + ("M-t" . dirvish-layout-toggle) + ("M-s" . dirvish-setup-menu) + ("M-e" . dirvish-emerge-menu) + ("M-j" . dirvish-fd-jump))) + +#+end_src +*** pdf-tools: pdf-viewer and support for dirvish +:PROPERTIES: +:CUSTOM_ID: h:b108dd3e-f34d-4ed3-98df-0bf9de055889 +:END: + +This enables pdf-previewing in dirvish and gives a much better pdf-viewer than is shipped normally by emacs. + +#+begin_src emacs-lisp + + (use-package pdf-tools + :init + (if (not (boundp 'pdf-tools-directory)) + (pdf-tools-install)) + :mode ("\\.pdf" . pdf-view-mode)) + +#+end_src + +*** Jupyter +:PROPERTIES: +:CUSTOM_ID: h:c15efae7-b884-4c97-8367-ccc7e7ed9ba8 +:END: + +This is a jupyter client. Using it is a bit cumbersome though, so I have not fully explored all features. + +#+begin_src emacs-lisp + + (use-package ein) + +#+end_src + +*** undo-tree +:PROPERTIES: +:CUSTOM_ID: h:1fc538d1-8c53-48b2-8652-66046f4bbbf8 +:END: + +Base emacs undo logic is very useful, but not easy to understand for me. I prefer undo-tree, which makes switching between branches easier and also allows quickly switching back to a much older state using the visualizer. + +Evil needs to be told to use this mode, see =(evil-set-undo-system 'undo-tree)= in [[#h:218376e8-086b-46bf-91b3-78295d5d440f][Evil/General.]] + +By default, I am not using undo-tree-mode in every buffer. This might change in the future, but for now this is fine. It can be enabled manually should the need arise. + +While we are at it, we are also setting up a persistent undo-file for every file that we are working with. + +#+begin_src emacs-lisp + + (use-package undo-tree + ;; :init (global-undo-tree-mode) + :bind (:map undo-tree-visualizer-mode-map + ("h" . undo-tree-visualize-switch-branch-left) + ("l" . undo-tree-visualize-switch-branch-left) + ("j" . undo-tree-visualize-redo) + ("k" . undo-tree-visualize-undo)) + :config + (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) + + (add-hook 'prog-mode-hook 'undo-tree-mode) + (add-hook 'text-mode-hook 'undo-tree-mode) + (add-hook 'org-mode-hook 'undo-tree-mode) + (add-hook 'latex-mode-hook 'undo-tree-mode) +#+end_src +*** Hydra +:PROPERTIES: +:CUSTOM_ID: h:b6c18dd0-3377-47ea-80c3-ac1486454e18 +:END: + +Hydra allows for the writing of macro-style functions. I have not yet looked into this all too much, but it seems to be a potent feature. + +#+begin_src emacs-lisp + + (use-package hydra) + +#+end_src + +**** Text scaling +:PROPERTIES: +:CUSTOM_ID: h:c5681884-7040-4b55-ab1b-5777631a0514 +:END: + +I only wrote this in order to try out hydra; rarely do I really need this. However, it can be useful for [[#h:4e11a845-a7bb-4eb5-b4ce-5b2f52e07425][Presentations]]. It simply scales the text size. + +#+begin_src emacs-lisp + + + ;; change the text size of the current buffer + (defhydra hydra-text-scale (:timeout 4) + "scale text" + ("j" text-scale-increase "in") + ("k" text-scale-decrease "out") + ("f" nil "finished" :exit t)) + +#+end_src +*** External Applications +:PROPERTIES: +:CUSTOM_ID: h:fff816a0-6d70-4bda-abab-833345e51100 +:END: +**** Obsidian +:PROPERTIES: +:CUSTOM_ID: h:9335d32d-bf08-4601-820d-f3d1f33f876f +:END: + +This provides an interface to Obsidian for Emacs - as much as I want to like it, I actually enjoy using the official Obsidian app more - even though that cannot be used by Emacs directly. + +My workflow for Obsidian is now as follows: + +1) create notes either in Emacs or Obsidian +2) look at them in the official client + + I hope that this package will improve, then I will come back to it one day. + +#+begin_src emacs-lisp + + ;; (use-package obsidian + ;; :ensure t + ;; :demand t + ;; :config + ;; (obsidian-specify-path swarsel-obsidian-vault-directory) + ;; (global-obsidian-mode t) + ;; :custom + ;; ;; This directory will be used for `obsidian-capture' if set. + ;; (obsidian-inbox-directory "Inbox") + ;; (bind-key (kbd "C-c M-o") 'obsidian-hydra/body 'obsidian-mode-map) + ;; :bind (:map obsidian-mode-map + ;; ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding. + ;; ("C-c C-o" . obsidian-follow-link-at-point) + ;; ;; Jump to backlinks + ;; ("C-c C-b" . obsidian-backlink-jump) + ;; ;; If you prefer you can use `obsidian-insert-link' + ;; ("C-c C-l" . obsidian-insert-wikilink))) + +#+end_src + +**** Anki +:PROPERTIES: +:CUSTOM_ID: h:5854c9a6-1319-4961-a112-75b1bf2e1f69 +:END: + +This section is here to make Anki usable from within Emacs - an endeavour that I have mostly given up on. + +***** Basic Anki setup +:PROPERTIES: +:CUSTOM_ID: h:d20559ed-7ada-4fea-a964-33bfd64b4549 +:END: + +#+begin_src emacs-lisp + + ;; (use-package anki-editor + ;; :after org + ;; :bind (:map org-mode-map + ;; ("" . anki-editor-cloze-region-auto-incr) + ;; ("" . anki-editor-cloze-region-dont-incr) + ;; ("" . anki-editor-reset-cloze-number) + ;; ("" . anki-editor-push-tree)) + ;; :hook (org-capture-after-finalize . anki-editor-reset-cloze-number) ; Reset cloze-number after each capture. + ;; :config + ;; (setq anki-editor-create-decks t ;; Allow anki-editor to create a new deck if it doesn't exist + ;; anki-editor-org-tags-as-anki-tags t) + + ;; (defun anki-editor-cloze-region-auto-incr (&optional arg) + ;; "Cloze region without hint and increase card number." + ;; (interactive) + ;; (anki-editor-cloze-region swarsel-anki-editor-cloze-number "") + ;; (setq swarsel-anki-editor-cloze-number (1+ swarsel-anki-editor-cloze-number)) + ;; (forward-sexp)) + ;; (defun anki-editor-cloze-region-dont-incr (&optional arg) + ;; "Cloze region without hint using the previous card number." + ;; (interactive) + ;; (anki-editor-cloze-region (1- swarsel-anki-editor-cloze-number) "") + ;; (forward-sexp)) + ;; (defun anki-editor-reset-cloze-number (&optional arg) + ;; "Reset cloze number to ARG or 1" + ;; (interactive) + ;; (setq swarsel-anki-editor-cloze-number (or arg 1))) + ;; (defun anki-editor-push-tree () + ;; "Push all notes under a tree." + ;; (interactive) + ;; (anki-editor-push-notes '(4)) + ;; (anki-editor-reset-cloze-number)) + ;; ;; Initialize + ;; (anki-editor-reset-cloze-number) + ;; ) + + ;; (require 'anki-editor) + +#+end_src + +***** Own Anki functions +:PROPERTIES: +:CUSTOM_ID: h:64242e95-6454-4330-bcb9-15353083bade +:END: + +- These functions enable you to quickly set the destination note type and deck + +#+begin_src emacs-lisp + + ;; (defvar swarsel-anki-deck nil) + ;; (defvar swarsel-anki-notetype nil) + ;; (defvar swarsel-anki-fields nil) + + ;; (defun swarsel-anki-set-deck-and-notetype () + ;; (interactive) + ;; (setq swarsel-anki-deck (completing-read "Choose a deck: " + ;; (sort (anki-editor-deck-names) #'string-lessp))) + ;; (setq swarsel-anki-notetype (completing-read "Choose a note type: " + ;; (sort (anki-editor-note-types) #'string-lessp))) + ;; (setq swarsel-anki-fields (progn + ;; (anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,swarsel-anki-notetype))))) + ;; ) + + ;; (defun swarsel-anki-make-template-string () + ;; (if (not swarsel-anki-deck) + ;; (call-interactively 'swarsel-anki-set-deck-and-notetype)) + ;; (setq swarsel-temp swarsel-anki-fields) + ;; (concat (concat "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: " swarsel-anki-notetype "\n:ANKI_DECK: " swarsel-anki-deck "\n:END:\n** ")(pop swarsel-temp) "\n%?\n** " (mapconcat 'identity swarsel-temp "\n\n** ") "\n\n")) + + ;; (defun swarsel-today() + ;; (format-time-string "%Y-%m-%d")) + + ;; (defun swarsel-obsidian-daily () + ;; (interactive) + ;; (if (not (file-exists-p (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))) + ;; (write-region "" nil (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)) + ;; ) + ;; (find-file (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))) + +#+end_src + +*** Email +:PROPERTIES: +:CUSTOM_ID: h:2f333330-b19d-4f64-85ea-146ff28667e8 +:END: +**** make sure mu4e is found +:PROPERTIES: +:CUSTOM_ID: h:48fde614-7cd0-4764-a7ac-0dae60d8b65a +:END: + +This seems not to be needed - I do not yet dare to delete it though. + +#+begin_src emacs-lisp + + ;; (let ((mu4epath + ;; (concat + ;; (f-dirname + ;; (file-truename + ;; (executable-find "mu"))) + ;; "/../share/emacs/site-lisp/mu4e"))) + ;; (when (and + ;; (string-prefix-p "/nix/store/" mu4epath) + ;; (file-directory-p mu4epath)) + ;; (add-to-list 'load-path mu4epath))) + +#+end_src + +**** mu4e +:PROPERTIES: +:CUSTOM_ID: h:b92a18cf-eec3-4605-a8c2-37133ade3574 +:END: + +In this section we are setting up mu4e, a mail client for emacs using mu with mbsync as backend. The mail accounts themselves are setup in the NixOS configuration, so we only need to add Emacs specific settings here. + +The hook functions are defined here: [[#h:34506761-06b9-43b5-a818-506d9b3faf28][mu4e functions]] + +#+begin_src emacs-lisp + + (use-package mu4e + :ensure nil + ;; :load-path "/usr/share/emacs/site-lisp/mu4e/" + ;;:defer 20 ; Wait until 20 seconds after startup + :config + + ;; This is set to 't' to avoid mail syncing issues when using mbsync + (setq send-mail-function 'sendmail-send-it) + (setq mu4e-change-filenames-when-moving t) + (setq mu4e-mu-binary (executable-find "mu")) + (setq mu4e-hide-index-messages t) + + (setq mu4e-update-interval 180) + (setq mu4e-get-mail-command "mbsync -a") + (setq mu4e-maildir "~/Mail") + + ;; enable inline images + (setq mu4e-view-show-images t) + ;; use imagemagick, if available + (when (fboundp 'imagemagick-register-types) + (imagemagick-register-types)) + + (setq mu4e-drafts-folder "/Drafts") + (setq mu4e-sent-folder "/Sent Mail") + (setq mu4e-refile-folder "/All Mail") + (setq mu4e-trash-folder "/Trash") + + (setq mu4e-maildir-shortcuts + '((:maildir "/leon/Inbox" :key ?1) + (:maildir "/nautilus/Inbox" :key ?2) + (:maildir "/mrswarsel/Inbox" :key ?3) + (:maildir "/Sent Mail" :key ?s) + (:maildir "/Trash" :key ?t) + (:maildir "/Drafts" :key ?d) + (:maildir "/All Mail" :key ?a))) + + (setq user-mail-address "leon@swarsel.win" + user-full-name "Leon Schwarzäugl") + + + (setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com))) + + + (add-hook 'mu4e-compose-mode-hook #'swarsel/mu4e-send-from-correct-address) + (add-hook 'mu4e-compose-post-hook #'swarsel/mu4e-restore-default) +#+end_src + +**** mu4e-alert +:PROPERTIES: +:CUSTOM_ID: h:43209eeb-5d46-472e-b7c2-58a3fb465199 +:END: + +This adds the simple utility of sending desktop notifications whenever a new mail is received. I am using =libnotify= because I want to use this with =notify-send=. + +#+begin_src emacs-lisp + + (use-package mu4e-alert + :config + (setq mu4e-alert-set-default-style 'libnotify)) + + (add-hook 'after-init-hook #'mu4e-alert-enable-notifications) + + (mu4e t) +#+end_src + +*** Calendar +:PROPERTIES: +:CUSTOM_ID: h:c760f04e-622f-4b3e-8916-53ca8cce6edc +:END: + +This provides a beautiful calender to emacs. + +Yes, I am aware that I am exposing my university-calendar to the public here. I can imagine worse things ;) if you however know how to obscure this, let me know! + +#+begin_src emacs-lisp + + (use-package org-caldav + :init + ;; set org-caldav-sync-initalization + (setq swarsel-caldav-synced 0) + (setq org-caldav-url "https://stash.swarsel.win/remote.php/dav/calendars/Swarsele") + (setq org-caldav-calendars + '((:calendar-id "personal" + :inbox "~/Calendars/leon_cal.org"))) + ;; (setq org-caldav-backup-file "~/org-caldav/org-caldav-backup.org") + ;; (setq org-caldav-save-directory "~/org-caldav/") + + :config + (setq org-icalendar-alarm-time 1) + ;; This makes sure to-do items as a category can show up on the calendar + (setq org-icalendar-include-todo t) + ;; This ensures all org "deadlines" show up, and show up as due dates + (setq org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)) + ;; This ensures "scheduled" org items show up, and show up as start times + (setq org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo)) + ) + + (use-package calfw + :ensure nil + :bind ("C-c A" . swarsel/open-calendar) + :init + (use-package calfw-cal + :ensure nil) + (use-package calfw-org + :ensure nil) + (use-package calfw-ical + :ensure nil) + :config + (bind-key "g" 'cfw:refresh-calendar-buffer cfw:calendar-mode-map) + (bind-key "q" 'evil-quit cfw:details-mode-map) + ;; (custom-set-faces + ;; '(cfw:face-title ((t (:foreground "#f0dfaf" :weight bold :height 65)))) + ;; ) + ) + + (defun swarsel/open-calendar () + (interactive) + (unless (eq swarsel-caldav-synced 1) (org-caldav-sync) (setq swarsel-caldav-synced 1)) + ;; (select-frame (make-frame '((name . "calendar")))) ; makes a new frame and selects it + ;; (set-face-attribute 'default (selected-frame) :height 65) ; reduces the font size of the new frame + (cfw:open-calendar-buffer + :contents-sources + (list + (cfw:org-create-source "Purple") ; orgmode source + (cfw:ical-create-source "TISS" "https://tiss.tuwien.ac.at/events/rest/calendar/personal?locale=de&token=4463bf7a-87a3-490a-b54c-99b4a65192f3" "Cyan")))) + +#+end_src + +*** Dashboard: emacs startup screen +:PROPERTIES: +:CUSTOM_ID: h:48f5be2b-b3d2-4276-bd49-2630733f23d5 +:END: + +This sets up the =dashboard=, which is really quite useless. But, it looks cool and makes me happy whenever I start an emacsclient without a file name as argument :) + +#+begin_src emacs-lisp + + (use-package dashboard + :ensure t + :config + (dashboard-setup-startup-hook) + ;; (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))) + (setq dashboard-display-icons-p t ;; display icons on both GUI and terminal + dashboard-icon-type 'nerd-icons ;; use `nerd-icons' package + dashboard-set-file-icons t + dashboard-items '((recents . 5) + (projects . 5) + (agenda . 5)) + dashboard-set-footer nil + dashboard-banner-logo-title "Welcome to SwarsEmacs!" + dashboard-image-banner-max-height 300 + dashboard-startup-banner "~/.dotfiles/wallpaper/swarsel.png" + dashboard-projects-backend 'projectile + dashboard-projects-switch-function 'magit-status + dashboard-set-navigator t + dashboard-startupify-list '(dashboard-insert-banner + dashboard-insert-newline + dashboard-insert-banner-title + dashboard-insert-newline + dashboard-insert-navigator + dashboard-insert-newline + dashboard-insert-init-info + dashboard-insert-items + ) + dashboard-navigator-buttons + `(;; line1 + ((,"" + "SwarselSocial" + "Browse Swarsele" + (lambda (&rest _) (browse-url "instagram.com/Swarsele"))) + + (,"" + "SwarselSound" + "Browse SwarselSound" + (lambda (&rest _) (browse-url "sound.swarsel.win")) ) + (,"" + "SwarselSwarsel" + "Browse Swarsel" + (lambda (&rest _) (browse-url "github.com/Swarsel")) ) + (,"" + "SwarselStash" + "Browse SwarselStash" + (lambda (&rest _) (browse-url "stash.swarsel.win")) ) + (,"󰫑" + "SwarselSport" + "Browse SwarselSports" + (lambda (&rest _) (browse-url "social.parkour.wien/@Lenno"))) + ) + ( + (,"󱄅" + "swarsel.win" + "Browse swarsel.win" + (lambda (&rest _) (browse-url "swarsel.win"))) + ) + ))) + + +#+end_src diff --git a/Wiki.org b/Wiki.org index 7bf5589..d9ecb85 100644 --- a/Wiki.org +++ b/Wiki.org @@ -62,6 +62,9 @@ This requires changes in multiple locations. As an example we will use an early * Build a firefox addon +1) app id can be found in the manifest.json file of the .xpi (.xpi is just a normal archive) +2) url can be found by copy url of the "add extension" button on the addon page +3) the rest of the information is also found in the manifest.json, but might not be needed #+begin_src nix configuration.nix programs.firefox = { @@ -159,3 +162,28 @@ This requires changes in multiple locations. As an example we will use an early * Patch a utilty for nix paths: See https://drakerossman.com/blog/how-to-patch-a-package-source-on-nixos +* let-block for overriding a package in nixpkgs (here: replacing airsonic with airsonic-advanced) + +This can be useful if a module does not let you use your own package yourself. + +#+begin_src nix :tangle no + + pkgs = import nixpkgs { inherit system; + overlays = [ emacs-overlay.overlay + nur.overlay + nixgl.overlay + (self: super: { + airsonic = super.airsonic.overrideAttrs (_: rec { + version = "11.0.2-kagemomiji"; + name = "airsonic-advanced-${version}"; + src = super.fetchurl { + url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/11.0.2/airsonic.war"; + sha256 = "PgErtEizHraZgoWHs5jYJJ5NsliDd9VulQfS64ackFo="; + }; + }); + }) + ]; + config.allowUnfree = true; + }; + +#+end_src diff --git a/flake.lock b/flake.lock index e2af851..5f78c6b 100644 --- a/flake.lock +++ b/flake.lock @@ -917,27 +917,6 @@ "type": "github" } }, - "pia": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1687263775, - "narHash": "sha256-sSVNT3o+4RcdnUqdVloPc3UvM3LancTY6htyzAHrC0w=", - "ref": "development", - "rev": "190f8040cc4837fb6b3c43032711e1536ef2270b", - "revCount": 10, - "type": "git", - "url": "https://git.sr.ht/~rprospero/nixos-pia" - }, - "original": { - "ref": "development", - "type": "git", - "url": "https://git.sr.ht/~rprospero/nixos-pia" - } - }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": [ @@ -983,7 +962,6 @@ "nixpkgs": "nixpkgs_5", "nixpkgs-mautrix-signal": "nixpkgs-mautrix-signal", "nur": "nur", - "pia": "pia", "sops-nix": "sops-nix", "stylix": "stylix" } diff --git a/flake.nix b/flake.nix index f9269b0..450461e 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ # provides GL to non-NixOS hosts nixgl.url = github:guibou/nixGL; - # manages all themeing using Home-Manager + # manages all theming using Home-Manager stylix.url = github:danth/stylix; # nix secrets management @@ -47,12 +47,6 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - # privateinternetaccess nixos integration - not sure if I will keep using - pia = { - url = "git+https://git.sr.ht/~rprospero/nixos-pia?ref=development"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - # provides expressions for mautrix-signal nixpkgs-mautrix-signal ={ url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal; @@ -88,7 +82,6 @@ stylix, sops-nix, lanzaboote, - pia, nixpkgs-mautrix-signal, nix-gaming, nixos-hardware, @@ -255,7 +248,6 @@ specialArgs = {inherit inputs pkgs; }; modules = [ sops-nix.nixosModules.sops - pia.nixosModule ./profiles/server1/transmission/nixos.nix ]; }; @@ -345,14 +337,6 @@ packages.x86_64-linux = { - proxmox-lxc = nixos-generators.nixosGenerate { - inherit system; - modules = [ - ./profiles/server1/TEMPLATE/nixos.nix - ]; - format = "proxmox-lxc"; - }; - }; }; diff --git a/index.html b/index.html new file mode 100644 index 0000000..31e2e0c --- /dev/null +++ b/index.html @@ -0,0 +1,12574 @@ + + + + + + + +SwarselSystems: NixOS + Emacs Configuration + + + + + +
+

SwarselSystems: NixOS + Emacs Configuration

+
+

Table of Contents

+
+ +
+
+

+This file has 40644 words spanning 10489 lines and was last revised on 2024-06-20 18:05:38 +0200. +

+ +

+In order to have working links and macros when viewing this file online, you might want to switch to the html version. +

+
+

1. Introduction (no code)

+
+

+This literate configuration file holds the entirety of all configuration files for both NixOS as well as home manager across all machines that I currently use. It also holds an extensive Emacs configuration +

+ +

+I used to have two separate files (Emacs.org and Nixos.org) because the NixOS setting for installing Emacs packages used to break if it found UTF-8 characters in .el files but not in .org files. Hence I used to pass Emacs.org to that function rather than init.el. This seems to be fixed now however and I was finally able to consolidate both files into one. +

+ +

+This configuration is part of a NixOS system that is fully declarative and can be found here: +

+ + + +

+The literate configuration lets me explain my choices to my future self as well as you, the reader. I go to great lengths to explain the choices for all configuration steps that I take in order for me to pay due diligence in crafting my setup, and not simply copying big chunks of other peoples code. Also, the literate configuration approach is very convenient to me as I only need to keep of (ideally) a single file to manage all of my configuration. I hope that this documentation will make it easier for beginners to get into Emacs and NixOS as I know it can be a struggle in the beginning. +

+ +

+This file is structured as follows: +

+ +
    +
  • Introduction (no code) +This is the block you are currently in. It holds no code that actually builds the system, it just outlines the general approach and explains my rough mentality
  • + +
  • Noweb-Ref blocks +This section hold code that can be templated at other parts of the configuration. This is mostly used for the NixOS side of the configuration where I define my host systems that usually have a lot in common.
  • + +
  • System +This section holds all configuration options that apply to NixOS or Home Manager. In other words, here we are doing system and user level configuration.
  • + +
  • +Emacs +This section defines my Emacs configuration. For a while, I considered to use rycee's emacs-init module (https://github.com/nix-community/nur-combined/blob/master/repos/rycee/hm-modules/emacs-init.nix) to manage my Emacs configuration; I have since come to the conclusion that this would be a bad idea: at the moment, even though it might seem as I am very bound to the configuration file that you are currently reading, if I ever decide to change how I run my system, I can simply take the generated .nix and .el files and put them wherever I need them. This file only simplifies that generation without putting further restrictions on my. If I were however to switch to emacs-init then I would be indeed to some level confined to the nix ecosystem with my Emacs configuration, as I would no longer have a valid .org file to manage it with, instead generating an init.el directly from nix code. I like to keep that level of freedom for potential future use. Also, you will notice there is no package system setup in this configuration. This is because packages are automatically handled on the NixOS side by parsing the generated init.el file for package installs. +

    + +

    +My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2024-06-20 18:05:38 +0200) +

  • +
+ +
+
+system-configuration-options
+
+
+
+ +
+--prefix=/nix/store/lymgpfqr5dp1wc0khbcbhhjnxq8ccsy9-emacs-pgtk-20240521.0 --disable-build-details --with-modules --with-pgtk --with-compress-install --with-toolkit-scroll-bars --with-native-compilation --without-imagemagick --with-mailutils --without-small-ja-dic --with-tree-sitter --without-xinput2 --with-xwidgets --with-dbus --with-selinux
+
+ + +

+This file is not loaded by Emacs directly as the configuration (even though this would be possible) - instead, it generates two more files: +

+ +
    +
  • early-init.el +This file handle startup optimization and sets up the basic frame that I will be working in.
  • + +
  • init.el +This file handles the rest of the Emacs configuration.
  • +
+ +

+By using the configuration offered by this file, the file you are reading right now (SwarselSystems.org) will be freshly tangled on every file save. However, when you clone this configuration yourself and have not yet activated it, you need to tangle the file yourself. This can be done using the keybind C-c C-v t. Alternatively, execute the following block: +

+ +
+
+(org-babel-tangle)
+
+
+
+ +

+Lastly, I add this javascript bit to the file in order to have a darkmode toggle when exporting to html: +

+ +
+
(concat
+ "<script src=\"https://cdn.jsdelivr.net/npm/darkmode-js@1.5.7/lib/darkmode-js.min.js\"></script>\n"
+ "<script>\n"
+ "function addDarkmodeWidget() {\n"
+ "new Darkmode().showWidget();\n"
+"}\n"
+"window.addEventListener('load', addDarkmodeWidget);\n"
+ "</script>")
+
+
+ + + +
+
+ + + +
+

2. Noweb-Ref blocks

+
+

+These blocks are used in several places throughout the configurations, but not on all machines necessarily. For example, the theming section needs to be in a NixOS block on NixOS machines but in a home-manager block on non-NixOS. This serves to reduce code duplication. +

+
+ +
+

2.1. Non-NixOS

+
+

+These blocks are to be used on systems that are not running NixOS. For example, one such system would be a Fedora system running home manager, where the respective NixOS features might not be available. +

+
+ +
+

2.1.1. Theme

+
+

+This is where the theme for the whole OS is defined. This noweb-ref section cannot be copied to the general NixOS config for now since they are on different folder structure levels in the config, which would make the flake impure. +

+ +

+For styling, I am using the stylix NixOS module, loaded by flake. This package is really great, as it adds nix expressions for basically everything. Ever since switching to this, I did not have to play around with theming anywhere else. +

+ +
+
+stylix = {
+  base16Scheme = ../../wallpaper/swarsel.yaml;
+  # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
+  polarity = "dark";
+  opacity.popups = 0.5;
+  cursor = {
+    package = pkgs.capitaine-cursors;
+    name = "capitaine-cursors";
+    size = 16;
+  };
+  fonts = {
+    sizes = {
+      terminal = 10;
+      applications = 11;
+    };
+    serif = {
+      # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
+      package = pkgs.cantarell-fonts;
+      # package = pkgs.montserrat;
+      name = "Cantarell";
+      # name = "FiraCode Nerd Font Propo";
+      # name = "Montserrat";
+    };
+
+    sansSerif = {
+      # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
+      package = pkgs.cantarell-fonts;
+      # package = pkgs.montserrat;
+      name = "Cantarell";
+      # name = "FiraCode Nerd Font Propo";
+      # name = "Montserrat";
+    };
+
+    monospace = {
+      package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
+      name = "FiraCode Nerd Font Mono";
+    };
+
+    emoji = {
+      package = pkgs.noto-fonts-emoji;
+      name = "Noto Color Emoji";
+    };
+  };
+};
+
+
+
+
+
+
+
+
+

2.1.2. Waybar items - LAPTOPS

+
+

+This noweb-ref block defines some aspects of my waybar configuration. Mainly, it adds the battery module to the waybar, which is no needed on PCs. +

+ +

+The most part of this configuration is done here: Waybar +

+ +
+
+programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
+                                                  "mpris"
+                                                  "custom/left-arrow-light"
+                                                  "network"
+                                                  "custom/left-arrow-dark"
+                                                  "pulseaudio"
+                                                  "custom/left-arrow-light"
+                                                  "custom/pseudobat"
+                                                  "battery"
+                                                  "custom/left-arrow-dark"
+                                                  "group/hardware"
+                                                  "custom/left-arrow-light"
+                                                  "clock#2"
+                                                  "custom/left-arrow-dark"
+                                                  "clock#1"
+                                                 ];
+
+
+
+
+
+
+

2.1.3. Waybar items - PC

+
+

+As stated above, this is the waybar configuration for PCs now. Here we do not need the battery module. However, this leads to a slight problem with theming: my waybar modules alternate their background-color between black and grey. The battery module is usually on grey background. If I were to simply delete that, I would now have two modules on black background. To avoid this, I define a pseudo-module custom/pseudobat that simply shows a static image and calls wlogout on right click. This wastes a little bit of screen space, but that is a price I am willing to pay for consistency. +

+ +

+The most part of this configuration is done here: Waybar +

+ +
+
+programs.waybar.settings.mainBar."custom/pseudobat"= {
+  format= "";
+  on-click-right= "wlogout -p layer-shell";
+};
+programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
+                                                  "mpris"
+                                                  "custom/left-arrow-light"
+                                                  "network"
+                                                  "custom/left-arrow-dark"
+                                                  "pulseaudio"
+                                                  "custom/left-arrow-light"
+                                                  "custom/pseudobat"
+                                                  "battery"
+                                                  "custom/left-arrow-dark"
+                                                  "group/hardware"
+                                                  "custom/left-arrow-light"
+                                                  "clock#2"
+                                                  "custom/left-arrow-dark"
+                                                  "clock#1"
+                                                 ];
+
+
+
+
+
+
+

2.1.4. Sway Startup commands

+
+

+This defines programs I want to have starting when I start the system +

+ +

+Part of the startup is also defined in Sway. The distinction is as follows. As this configuration also needs to work on systems that are running only home manager, I probably need to run nixGL or something similar on those systems to get these graphic apps to display properly. In this section we only define such graphical programs, in the other location we only put shell applications and such. +

+ +

+These other apps currently include: +

+
    +
  • spotifytui
  • +
  • kitty
  • +
+ +

+Do not that syncthingtray is also not mentioned here. It is installed as a home manager package that automatically starts at system start. +

+ +
+
+{ command = "nextcloud --background";}
+{ command = "discord --start-minimized";}
+{ command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
+{ command = "ANKI_WAYLAND=1 anki";}
+{ command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
+{ command = "nm-applet";}
+
+
+
+
+
+ +
+

2.1.5. gpg-agent

+
+

+This section holds most of the configuration needed for the gpg-agent. This allows me to use my Yubikey during normal system operation as well in Emacs (with some extra configuration here: Magit) +

+ +

+Also, there are some more NixOS related options here: Yubikey settings +

+ +

+I also enable the extra socket here for ssh agent forwarding. But I have not fully gotten it to work yet. +

+ +
+
+services.gpg-agent = {
+  enable = true;
+  enableSshSupport = true;
+  enableExtraSocket = true;
+  pinentryPackage = pkgs.pinentry-gtk2;
+  extraConfig = ''
+  allow-emacs-pinentry
+  allow-loopback-pinentry
+  '';
+  };
+
+
+
+
+
+
+ +
+

2.2. NixOS

+
+

+These settings are to be used only on full NixOS setups. +

+
+ +
+

2.2.1. Wrap with hardware-configuration

+
+

+This handles the automactically generated /etc/nixos/hardware-configuration.nix file that sets some hardware specific settings automatically upon creating the NixOS system. +

+ +

+This sections used to handle more imports, but at the moment, it is now pretty useless really. +

+ +
+
+imports =
+  [
+    ./hardware-configuration.nix
+  ];
+
+
+
+
+
+ +
+

2.2.2. Virtual hosts init

+
+

+This sections is for common NixoS settings that I use for my NixoS LXC images that I run on Proxmox. Proxmox requires special attention to run along with NixOS in any capacity. +

+ +
+
+
+services.xserver = {
+  layout = "us";
+  xkbVariant = "altgr-intl";
+};
+nix.settings.experimental-features = ["nix-command" "flakes"];
+proxmoxLXC.manageNetwork = true; # manage network myself
+proxmoxLXC.manageHostName = false; # manage hostname myself
+networking.useDHCP = true;
+networking.enableIPv6 = false;
+services.openssh = {
+  enable = true;
+  settings.PermitRootLogin = "yes";
+  listenAddresses = [{
+    port = 22;
+    addr = "0.0.0.0";
+  }];
+};
+users.users.root.openssh.authorizedKeys.keyFiles = [
+  ../../../secrets/keys/authorized_keys
+];
+
+system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
+
+environment.shellAliases = {
+  nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
+};
+
+
+
+ +

+This is again the hardware-configuration.nix wrap that you saw earlier, however for Proxmox systems we need to add some more NixOS modules for compatibility. +

+ +
+
+imports = [
+  (modulesPath + "/virtualisation/proxmox-lxc.nix")
+  ./hardware-configuration.nix
+];
+
+
+
+services.xserver = {
+  layout = "us";
+  xkbVariant = "altgr-intl";
+};
+nix.settings.experimental-features = ["nix-command" "flakes"];
+proxmoxLXC.manageNetwork = true; # manage network myself
+proxmoxLXC.manageHostName = false; # manage hostname myself
+networking.useDHCP = true;
+networking.enableIPv6 = false;
+services.openssh = {
+  enable = true;
+  settings.PermitRootLogin = "yes";
+  listenAddresses = [{
+    port = 22;
+    addr = "0.0.0.0";
+  }];
+};
+users.users.root.openssh.authorizedKeys.keyFiles = [
+  ../../../secrets/keys/authorized_keys
+];
+
+system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
+
+environment.shellAliases = {
+  nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
+};
+
+
+
+
+
+
+
+ +
+

2.3. flake.nix

+
+

+Handling the flake.nix file used to be a bit of a chore, since it felt like writing so much boilerplate code just to define new systems. The noweb-approach here makes this a little bit less painful. +

+ +

+These blocks are later inserted here: flake.nix template. Adding new flake inputs is very easy, you just add them to Inputs & Inputs@Outputs first by name in the first source-block, and then the path in the second source-block. Any variables to be set for the host configuration are done in let, and the specific setup is done in either nixosConfigurations (for NixOS systems), homeConfigurations (for home-manager systems), or nixOnDroidConfigurations (for Nix on Android). There is also the nixos-generators section that currently just defines a Proxmox LXC image. +

+
+ +
+

2.3.1. Inputs & Inputs@Outputs

+
+

+Here we define inputs and outputs of the flake. First, the following list is for the outputs of the flake. +

+ +

+Format: <name>, +

+ +

+Mind the comma at the end. You need this because the ... is being passed as the last argument in the template at flake.nix template. +

+ +
+
+nixpkgs,
+home-manager,
+nix-on-droid,
+nixos-generators,
+emacs-overlay,
+nur,
+nixgl,
+stylix,
+sops-nix,
+lanzaboote,
+nixpkgs-mautrix-signal,
+nix-gaming,
+nixos-hardware,
+nix-alien,
+
+
+
+ +

+Here, just add the input names, urls and other options that are needed, like nixpkgs.follows. By using the latter option, you tell the package to not provide it's own package repository, but instead 'nest' itself into another, which is very useful. +A short overview over each input and what it does: +

+ +
    +
  • nixkpkgs +This is the base repository that I am following for all packages. I follow the unstable branch.
  • +
  • home-manager +This handles user-level configuration and mostly provides dotfiles that are generated and symlinked to ~/.config/.
  • +
  • NUR +The nix user repository contains user provided modules, packages and expressions. These are not audited by the nix community, so be aware of supply chain vulnerabilities when using those. I am only really using rycee's firefox addons from there which saves me a lot of hassle, and it seems to be a safe resource.
  • +
  • nixGL +This solves the problem that nix has with "OpenGL", as libraries are not linked and programs will often fail to find drivers. But I do not fully understand what it does. All I know is that I usually have to use this on non-NIxoS systems.
  • +
  • stylix +As described before, this handles all theme related options.
  • +
  • sops-nix +This provides declarative secrets management for NixOS and home manager using sops and age keys. It is a bit more cumbersome to use on home manager systems - which is a bother because I then have to resort to that configuration to keep everything supported - but it is super practical and really the primary reason why it makes sense for me to go for NixOS, as I do not have to do any extra secrets provisioning.
  • +
  • Lanzaboote +Provides secure boot for NixOS. Needed for my Surface Pro 3.
  • +
  • nix-on-droid +This brings nix to android in an app that is similar to tmux! Of course most of the configuration does not apply to this, but it is still neat to have!
  • +
  • nixos-generators +Provides me with images that I can use to create LXCs on Proxmox.
  • +
  • nix-gaming +Provides some gaming related tweaks for NixOS
  • +
  • nixos-hardware +Provides specific hardware setting for some hardware configurations. For example, this sets some better defaults for my Lenovo Thinkpad P14s Gen2.
  • +
  • nix-alien +This is supposed to allow me to run unpatched libraries directly without a need for ELF patching or resorting to steam-run. However, I have not yet gotten this to work.
  • +
+ + +
+
+nixpkgs.url = github:nixos/nixpkgs/nixos-unstable;
+
+
+# user-level configuration
+home-manager = {
+  url = github:nix-community/home-manager;
+  inputs.nixpkgs.follows = "nixpkgs";
+};
+
+# overlay to access bleeding edge emacs
+emacs-overlay = {
+  url = github:nix-community/emacs-overlay;
+  inputs.nixpkgs.follows = "nixpkgs";
+};
+
+# nix user repository
+# i use this mainly to not have to build all firefox extensions
+# myself as well as for the emacs-init package (tbd)
+nur.url = github:nix-community/NUR;
+
+# provides GL to non-NixOS hosts
+nixgl.url = github:guibou/nixGL;
+
+# manages all theming using Home-Manager
+stylix.url = github:danth/stylix;
+
+# nix secrets management
+sops-nix.url = github:Mic92/sops-nix;
+
+# enable secure boot on NixOS
+lanzaboote.url = github:nix-community/lanzaboote;
+
+# nix for android
+nix-on-droid = {
+  url = github:t184256/nix-on-droid/release-23.05;
+  inputs.nixpkgs.follows = "nixpkgs";
+};
+
+# generate NixOS images
+nixos-generators = {
+  url = github:nix-community/nixos-generators;
+  inputs.nixpkgs.follows = "nixpkgs";
+};
+
+# provides expressions for mautrix-signal
+nixpkgs-mautrix-signal ={
+  url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal;
+};
+
+# patches for gaming on nix
+nix-gaming = {
+  url = github:fufexan/nix-gaming;
+};
+
+# hardware quirks on nix
+nixos-hardware = {
+  url = github:NixOS/nixos-hardware/master;
+};
+
+# dynamic library loading
+nix-alien = {
+  url = github:thiagokokada/nix-alien;
+};
+
+
+
+
+
+
+

2.3.2. let

+
+

+Here I define a few variables that I need for my system specifications. First and foremost, pkgs, which gets passed the emacs-overlay, nur, and nixgl modules to it. With this, I can grab all these packages by referencing pkgs.<name> instead of having to put e.g. nixgl.auto.nixGLDefault. +

+ +

+I also define armpkgs, which are simply built for ARM hosts, i.e. my virtual machines that I have in the Oracle Cloud. +

+ +

+Lastly I define some common module lists that I can simply load depending on the fundamental system (NixOS vs. non-NixOS). +

+ +
+
+system = "x86_64-linux"; # not very portable, but I do not use other architectures at the moment
+pkgs = import nixpkgs { inherit system;
+                        overlays = [ emacs-overlay.overlay
+                                     nur.overlay
+                                     nixgl.overlay
+                                   ];
+                        config.allowUnfree = true;
+                      };
+
+# for ovm arm hosts
+armpkgs = import nixpkgs { system = "aarch64-linux";
+                        overlays = [ emacs-overlay.overlay
+                                     nur.overlay
+                                     nixgl.overlay
+                                   ];
+                        config.allowUnfree = true;
+                      };
+
+pkgsmautrix = import nixpkgs-mautrix-signal { inherit system;
+                        config.allowUnfree = true;
+                      };
+
+# NixOS modules that can only be used on NixOS systems
+nixModules = [ stylix.nixosModules.stylix
+               ./profiles/common/nixos.nix
+               # dynamic library loading
+               ({ self, system, ... }: {
+                 environment.systemPackages = with self.inputs.nix-alien.packages.${system}; [
+                   nix-alien
+                 ];
+                 # needed for `nix-alien-ld`
+                 programs.nix-ld.enable = true;
+                 })
+               ];
+
+# Home-Manager modules wanted on non-NixOS systems
+homeModules = [ stylix.homeManagerModules.stylix
+              ];
+# Home-Manager modules wanted on both NixOS and non-NixOS systems
+mixedModules = [ sops-nix.homeManagerModules.sops
+                 ./profiles/common/home.nix
+               ];
+
+
+
+
+
+
+

2.3.3. nixosConfigurations

+
+

+This section is the biggest pain point of the configuration. For every system, I have one of these. I know there are better ways to go about this, but I did not find the time yet to look into this further. For now, enjoy this meter-long list +

+ +
+
+onett = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = nixModules ++ [
+    ./profiles/onett/nixos.nix
+    home-manager.nixosModules.home-manager
+    {
+      home-manager.users.swarsel.imports = mixedModules ++ [
+        ./profiles/onett/home.nix
+      ];
+    }
+  ];
+};
+
+sandbox = nixpkgs.lib.nixosSystem {
+  pkgs = pkgsmautrix;
+  specialArgs.unstable = nixpkgs-mautrix-signal;
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/sandbox/nixos.nix
+  ];
+};
+
+twoson = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = nixModules ++ [
+    ./profiles/twoson/nixos.nix
+    home-manager.nixosModules.home-manager
+    {
+      home-manager.users.swarsel.imports = mixedModules ++ [
+        ./profiles/twoson/home.nix
+      ];
+    }
+  ];
+};
+
+threed = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = nixModules ++ [
+    lanzaboote.nixosModules.lanzaboote
+    ./profiles/threed/nixos.nix
+    home-manager.nixosModules.home-manager
+    {
+      home-manager.users.swarsel.imports = mixedModules ++ [
+        ./profiles/threed/home.nix
+      ];
+    }
+  ];
+};
+
+fourside = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = nixModules ++ [
+    nixos-hardware.nixosModules.lenovo-thinkpad-p14s-amd-gen2
+    ./profiles/fourside/nixos.nix
+    home-manager.nixosModules.home-manager
+    {
+      home-manager.users.swarsel.imports = mixedModules ++ [
+        ./profiles/fourside/home.nix
+      ];
+    }
+  ];
+};
+
+stand = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = nixModules ++ [
+    ./profiles/stand/nixos.nix
+    home-manager.nixosModules.home-manager
+    {
+      home-manager.users.homelen.imports = mixedModules ++ [
+        ./profiles/stand/home.nix
+      ];
+    }
+  ];
+};
+
+nginx = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/nginx/nixos.nix
+  ];
+};
+
+calibre = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/calibre/nixos.nix
+  ];
+};
+
+jellyfin = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    # sops-nix.nixosModules.sops
+    ./profiles/server1/jellyfin/nixos.nix
+  ];
+};
+
+transmission = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/transmission/nixos.nix
+  ];
+};
+
+matrix = nixpkgs.lib.nixosSystem {
+  # specialArgs = {inherit pkgsmautrix; };
+  pkgs = pkgsmautrix;
+  # this is to import a service module that is not on nixpkgs
+  # this way avoids infinite recursion errors
+  specialArgs.unstable = nixpkgs-mautrix-signal;
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/matrix/nixos.nix
+  ];
+};
+
+sound = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/sound/nixos.nix
+  ];
+};
+
+spotifyd = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/spotifyd/nixos.nix
+  ];
+};
+
+paperless = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/server1/paperless/nixos.nix
+  ];
+};
+
+#ovm swarsel
+sync = nixpkgs.lib.nixosSystem {
+  specialArgs = {inherit inputs pkgs; };
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/remote/oracle/sync/nixos.nix
+  ];
+};
+
+#ovm swarsel
+swatrix = nixpkgs.lib.nixosSystem {
+  # specialArgs = {inherit pkgsmautrix; };
+  pkgs = pkgsmautrix;
+  # this is to import a service module that is not on nixpkgs
+  # this way avoids infinite recursion errors
+  specialArgs.unstable = nixpkgs-mautrix-signal;
+  modules = [
+    sops-nix.nixosModules.sops
+    ./profiles/remote/oracle/matrix/nixos.nix
+  ];
+};
+
+
+
+
+
+

2.3.4. homeConfigurations

+
+

+In contrast, this defines home-manager systems, which I only have one of. +

+ +
+
+"leons@PCisLee" = home-manager.lib.homeManagerConfiguration {
+  inherit pkgs;
+  modules = homeModules ++ mixedModules ++ [
+    ./profiles/surface/home.nix
+  ];
+};
+
+
+
+
+
+ +
+

2.3.5. nixOnDroidConfigurations

+
+

+Nix on Android also demands an own flake output, which is provided here. +

+ +
+
+default = nix-on-droid.lib.nixOnDroidConfiguration {
+  modules = [
+    ./profiles/mysticant/configuration.nix
+  ];
+};
+
+
+
+
+
+ +
+

2.3.6. nixos-generators

+
+

+This builds my proxmox template. It is defined as a separate output so that I can already apply some rudimentary configuration before even setting up the system. +

+ +

+Usage: +

+ +
+
+nix build ~/.dotfiles/#proxmox-lxc
+
+
+
+ +

+The resulting image can then be loaded in Proxmox. +

+ +
+
+proxmox-lxc = nixos-generators.nixosGenerate {
+  inherit system;
+  modules = [
+     ./profiles/server1/TEMPLATE/nixos.nix
+  ];
+  format = "proxmox-lxc";
+};
+
+
+
+
+
+
+
+ +
+

3. System

+
+
+ +
+

3.1. System specific configuration

+
+

+This section mainly exists house different `configuration.nix` files for system level configurations of NixOS systems as well as `home.nix` for user level configurations on all systems. +

+ +

+Important: Think about if a settings really needs to go into this area - chances are that the settings can also go to the general settings, which is to be preferred in order to reduce code duplication. +

+
+ +
+

3.1.1. Template (for new machines)

+
+

+This section holds the minimum configuration that is needed on a new host. These assume a NixOS machine (so not standalone home-manager on a non-NixOS host), as this is the setting that I will most likely use in the future now. All of these blocks need to be updated, with entries called TEMPLATE mostly needed to be filled with host-/user-specific values or other inputs. If TEMPLATE is given in a comment section, see the provided values as likely defaults. The TEMPLATE comments should afterwards be deleted for clarity. +

+ +

+If a non-NixOS host must be used, check the Surface configuration for pointers. Most likely the waybar settings need to be adjusted, since non-NixOS (as of writing this) fails to display drawers in the waybar properly. +

+ +

+No matter what you do, check the initial /etc/nixos/configuration.nix for notable changes that might emerge in future versions of nix. +

+
+ +
    +
  1. NixOS
    +
    +
    +
    +{ config, lib, pkgs, inputs, ... }:
    +
    +{
    +
    +
    +  imports =
    +    [
    +      ./hardware-configuration.nix
    +    ];
    +
    +
    +  services = {
    +    getty.autologinUser = "TEMPLATE";
    +    greetd.settings.initial_session.user="TEMPLATE";
    +  };
    +
    +  # Bootloader
    +  boot.loader.grub.enable = true;
    +  boot.loader.grub.device = "/dev/sda"; # TEMPLATE - if only one disk, this will work
    +  boot.loader.grub.useOSProber = true;
    +
    +  # --------------------------------------
    +  # you might need a configuration like this instead:
    +  # Bootloader
    +  # boot.loader.grub.enable = true;
    +  # boot.loader.grub.devices = ["nodev" ];
    +  # boot.loader.grub.useOSProber = true;
    +  # boot.kernelPackages = pkgs.linuxPackages_latest;
    +  # --------------------------------------
    +
    +  networking.hostName = "TEMPLATE"; # Define your hostname.
    +
    +  stylix.image = ../../wallpaper/TEMPLATEwp.png;
    +
    +  stylix = {
    +    base16Scheme = ../../wallpaper/swarsel.yaml;
    +    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
    +    polarity = "dark";
    +    opacity.popups = 0.5;
    +    cursor = {
    +      package = pkgs.capitaine-cursors;
    +      name = "capitaine-cursors";
    +      size = 16;
    +    };
    +    fonts = {
    +      sizes = {
    +        terminal = 10;
    +        applications = 11;
    +      };
    +      serif = {
    +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
    +        package = pkgs.cantarell-fonts;
    +        # package = pkgs.montserrat;
    +        name = "Cantarell";
    +        # name = "FiraCode Nerd Font Propo";
    +        # name = "Montserrat";
    +      };
    +
    +      sansSerif = {
    +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
    +        package = pkgs.cantarell-fonts;
    +        # package = pkgs.montserrat;
    +        name = "Cantarell";
    +        # name = "FiraCode Nerd Font Propo";
    +        # name = "Montserrat";
    +      };
    +
    +      monospace = {
    +        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
    +        name = "FiraCode Nerd Font Mono";
    +      };
    +
    +      emoji = {
    +        package = pkgs.noto-fonts-emoji;
    +        name = "Noto Color Emoji";
    +      };
    +    };
    +  };
    +
    +
    +
    +
    +  # Configure keymap in X11 (only used for login)
    +  services.xserver = {
    +    layout = "us";
    +    xkbVariant = "altgr-intl";
    +  };
    +
    +  users.users.TEMPLATE = {
    +    isNormalUser = true;
    +    description = "TEMPLATE";
    +    extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
    +    packages = with pkgs; [];
    +  };
    +
    +  environment.systemPackages = with pkgs; [
    +  ];
    +
    +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
    +
    +}
    +
    +
    +
    +
    +
  2. + +
  3. Home Manager
    +
    +
    +
    +{ config, pkgs, lib, fetchFromGitHub, ... }:
    +
    +{
    +
    +
    +  services.gpg-agent = {
    +    enable = true;
    +    enableSshSupport = true;
    +    enableExtraSocket = true;
    +    pinentryPackage = pkgs.pinentry-gtk2;
    +    extraConfig = ''
    +    allow-emacs-pinentry
    +    allow-loopback-pinentry
    +    '';
    +    };
    +
    +  home = {
    +    username = "TEMPLATE";
    +    homeDirectory = "/home/TEMPLATE";
    +    stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing.
    +    keyboard.layout = "us"; # TEMPLATE
    +    home.packages = with pkgs; [
    +      # ---------------------------------------------------------------
    +      # if schildichat works on this machine, use it, otherwise go for element
    +      # element-desktop
    +      # ---------------------------------------------------------------
    +    ];
    +  };
    +  # update path if the sops private key is stored somewhere else
    +  sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];
    +
    +  # waybar config - TEMPLATE - update for cores and temp
    +  programs.waybar.settings.mainBar = {
    +    #cpu.format = "{icon0} {icon1} {icon2} {icon3}";
    +    cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
    +    temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
    +  };
    +
    +  # -----------------------------------------------------------------
    +  # is this machine always connected to power? If yes, use this block:
    +  # 
    +  # programs.waybar.settings.mainBar."custom/pseudobat"= {
    +  #   format= "";
    +  #   on-click-right= "wlogout -p layer-shell";
    +  # };
    +  # programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
    +  #                                                   "mpris"
    +  #                                                   "custom/left-arrow-light"
    +  #                                                   "network"
    +  #                                                   "custom/left-arrow-dark"
    +  #                                                   "pulseaudio"
    +  #                                                   "custom/left-arrow-light"
    +  #                                                   "custom/pseudobat"
    +  #                                                   "battery"
    +  #                                                   "custom/left-arrow-dark"
    +  #                                                   "group/hardware"
    +  #                                                   "custom/left-arrow-light"
    +  #                                                   "clock#2"
    +  #                                                   "custom/left-arrow-dark"
    +  #                                                   "clock#1"
    +  #                                                  ];
    +  # 
    +  # -----------------------------------------------------------------
    +
    +  # -----------------------------------------------------------------
    +  # if not always connected to power (laptop), use this (default):
    +
    +  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
    +                                                    "mpris"
    +                                                    "custom/left-arrow-light"
    +                                                    "network"
    +                                                    "custom/left-arrow-dark"
    +                                                    "pulseaudio"
    +                                                    "custom/left-arrow-light"
    +                                                    "custom/pseudobat"
    +                                                    "battery"
    +                                                    "custom/left-arrow-dark"
    +                                                    "group/hardware"
    +                                                    "custom/left-arrow-light"
    +                                                    "clock#2"
    +                                                    "custom/left-arrow-dark"
    +                                                    "clock#1"
    +                                                   ];
    +
    +  # -----------------------------------------------------------------
    +
    +  wayland.windowManager.sway= {
    +    config = rec {
    +      # update for actual inputs here,
    +      input = {
    +        "36125:53060:splitkb.com_Kyria_rev3" = {
    +          xkb_layout = "us";
    +          xkb_variant = "altgr-intl";
    +        };
    +        "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE
    +          xkb_layout = "us";
    +          xkb_options = "grp:win_space_toggle";
    +          # xkb_options = "ctrl:nocaps,grp:win_space_toggle";
    +          xkb_variant = "altgr-intl";
    +        };
    +        "type:touchpad" = {
    +          dwt = "enabled";
    +          tap = "enabled";
    +          natural_scroll = "enabled";
    +          middle_emulation = "enabled";
    +        };
    +
    +      };
    +
    +      output = {
    +        DP-1 = {
    +          mode = "2560x1440"; # TEMPLATE
    +          scale = "1";
    +          bg = "~/.dotfiles/wallpaper/TEMPLATE.png fill";
    +        };
    +      };
    +
    +      keybindings = let
    +        modifier = config.wayland.windowManager.sway.config.modifier;
    +      in {
    +        # TEMPLATE
    +        "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
    +        # "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\"";
    +      };
    +
    +      startup = [
    +
    +        { command = "nextcloud --background";}
    +        { command = "discord --start-minimized";}
    +        { command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
    +        { command = "ANKI_WAYLAND=1 anki";}
    +        { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
    +        { command = "nm-applet";}
    +
    +      ];
    +    };
    +  };
    +}
    +
    +
    +
    +
    +
  4. +
+
+ +
+

3.1.2. Physical hosts

+
+
+
    +
  1. Surface
    +
    +

    +My Surface Pro 3, only used for on-the-go university work. Be careful when pushing large changes to this machine, as it easily runs out of memory on large switches. At the moment the only machine running non-NixOS, so special care must be taken not to break this one during updates. +

    +
    + +
      +
    1. Channel setup
      +
      +

      +This installs nixGL, which is needed to run GL apps installed through home-manager, since this machine is not using NixOS. +

      + +

      +This is not super clean (because it is not fully replicative), but I do not really care. +

      + +
        +
      1. Install nixGL:
      2. +
      + +
      +
      nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
      +  nix-env -iA nixgl.auto.nixGLDefault   # or replace `nixGLDefault` with your desired wrapper
      +
      +
      + +

      +This is needed in order to use EGL. Prefix programs that use it with `nixGL` +

      +
      +
    2. + +
    3. Home manager
      +
      +

      +Special things to note here: We are running xcape to allow CAPS to act as CTRL and ESC. Also we are using nixGL in most places. +

      + +
      +
      +{ config, pkgs, lib, fetchFromGitHub, ... }:
      +
      +{
      +  programs.home-manager.enable = true;
      +  home.username = "leons";
      +  home.homeDirectory = "/home/leons";
      +
      +  home.stateVersion = "23.05"; # Please read the comment before changing.
      +
      +   stylix.image = ../../wallpaper/surfacewp.png;
      +
      +  stylix = {
      +    base16Scheme = ../../wallpaper/swarsel.yaml;
      +    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
      +    polarity = "dark";
      +    opacity.popups = 0.5;
      +    cursor = {
      +      package = pkgs.capitaine-cursors;
      +      name = "capitaine-cursors";
      +      size = 16;
      +    };
      +    fonts = {
      +      sizes = {
      +        terminal = 10;
      +        applications = 11;
      +      };
      +      serif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      sansSerif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      monospace = {
      +        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
      +        name = "FiraCode Nerd Font Mono";
      +      };
      +
      +      emoji = {
      +        package = pkgs.noto-fonts-emoji;
      +        name = "Noto Color Emoji";
      +      };
      +    };
      +  };
      +
      +
      +
      +
      +  nixpkgs = {
      +    config = {
      +      allowUnfree = true;
      +      allowUnfreePredicate = (_: true);
      +    };
      +  };
      +  services.xcape = {
      +    enable = true;
      +    mapExpression = {
      +      Control_L = "Escape";
      +    };
      +  };
      +  #keyboard config
      +  home.keyboard.layout = "us";
      +
      +  sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];
      +
      +  # waybar config
      +  programs.waybar.settings.mainBar.cpu.format = "{icon0} {icon1} {icon2} {icon3}";
      +
      +  programs.waybar.settings.mainBar.temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon3/temp3_input";
      +  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark" "mpris" "custom/left-arrow-light"
      +                                                    "network"
      +                                                    "custom/left-arrow-dark"
      +                                                    "pulseaudio"
      +                                                    "custom/left-arrow-light"
      +                                                    "battery"
      +                                                    "custom/left-arrow-dark"
      +                                                    "temperature"
      +                                                    "custom/left-arrow-light"
      +                                                    "disk"
      +                                                    "custom/left-arrow-dark"
      +                                                    "memory"
      +                                                    "custom/left-arrow-light"
      +                                                    "cpu"
      +                                                    "custom/left-arrow-dark"
      +                                                    "tray"
      +                                                    "custom/left-arrow-light"
      +                                                    "clock#2"
      +                                                    "custom/left-arrow-dark"
      +                                                    "clock#1" ];
      +  services.blueman-applet.enable = true;
      +  home.packages = with pkgs; [
      +    # nixgl.auto.nixGLDefault
      +    evince
      +    # nodejs_20
      +
      +    # messaging
      +    # we use gomuks for RAM preservation, but keep schildi around for files and images
      +  ];
      +
      +  programs.zsh.initExtra = "
      +export GPG_TTY=\"$(tty)\"
      +export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
      +gpgconf --launch gpg-agent
      +      ";
      +
      +  # sway config
      +  wayland.windowManager.sway= {
      +    config = rec {
      +      input = {
      +        "*" = {
      +          xkb_layout = "us";
      +          xkb_options = "ctrl:nocaps,grp:win_space_toggle";
      +          xkb_variant = "altgr-intl";
      +        };
      +        "type:touchpad" = {
      +          dwt = "enabled";
      +          tap = "enabled";
      +          natural_scroll = "enabled";
      +          middle_emulation = "enabled";
      +        };
      +      };
      +
      +      output = {
      +        eDP-1 = {
      +          mode = "2160x1440@59.955Hz";
      +          scale = "1";
      +          bg = "~/.dotfiles/wallpaper/surfacewp.png fill";
      +        };
      +      };
      +
      +      keybindings = let
      +        modifier = config.wayland.windowManager.sway.config.modifier;
      +      in {
      +        "${modifier}+F2"  = "exec brightnessctl set +5%";
      +        "${modifier}+F1"= "exec brightnessctl set 5%-";
      +        "${modifier}+n" = "exec sway output eDP-1 transform normal, splith";
      +        "${modifier}+Ctrl+p" = "exec nixGL wl-mirror eDP-1";
      +        "${modifier}+t" = "exec sway output eDP-1 transform 90, splitv";
      +        "${modifier}+XF86AudioLowerVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
      +        "${modifier}+XF86AudioRaiseVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
      +        "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkgomuks.sh\"";
      +      };
      +
      +      startup = [
      +        { command = "sleep 60 && nixGL nextcloud --background";}
      +        # { command = "sleep 60 && nixGL spotify";}
      +        { command = "sleep 60 && nixGL discord --start-minimized -enable-features=UseOzonePlatform -ozone-platform=wayland";}
      +        # { command = "sleep 60 && nixGL schildichat-desktop --hidden";}
      +        { command = "sleep 60 && nixGL syncthingtray --wait"; }
      +        { command = "sleep 60 && ANKI_WAYLAND=1 nixGL anki";}
      +        { command = "nm-applet --indicator";}
      +        { command = "sleep 60 && OBSIDIAN_USE_WAYLAND=1 nixGL obsidian -enable-features=UseOzonePlatform -ozone-platform=wayland";}
      +      ];
      +
      +      keycodebindings = {
      +        "124" = "exec systemctl suspend";
      +      };
      +    };
      +
      +    extraConfig = "
      +    exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05 map_to_output eDP-1
      +    exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05_Stylus map_to_output eDP-1
      +    ";
      +  };
      +}
      +
      +
      +
      +
      +
    4. +
    +
  2. + +
  3. Onett (Lenovo Y510P)
    +
    +

    +My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately. +

    +
    + +
      +
    1. NixOS
      +
      +
      +
      +{ config, lib, pkgs, inputs, ... }:
      +
      +{
      +
      +
      +  imports =
      +    [
      +      ./hardware-configuration.nix
      +    ];
      +
      +
      +  services = {
      +    greetd.settings.initial_session.user ="swarsel";
      +    xserver.videoDrivers = ["nvidia"];
      +  };
      +
      +
      +  hardware = {
      +    nvidia = {
      +      modesetting.enable = true;
      +      powerManagement.enable = true;
      +      prime = {
      +        intelBusId = "PCI:0:2:0";
      +        nvidiaBusId = "PCI:1:0:0";
      +        sync.enable = true;
      +      };
      +    };
      +    pulseaudio.configFile = pkgs.runCommand "default.pa" {} ''
      +                 sed 's/module-udev-detect$/module-udev-detect tsched=0/' \
      +                   ${pkgs.pulseaudio}/etc/pulse/default.pa > $out
      +                 '';
      +    bluetooth.enable = true;
      +  };
      +
      +  stylix.image = ../../wallpaper/lenovowp.png;
      +
      +  stylix = {
      +    base16Scheme = ../../wallpaper/swarsel.yaml;
      +    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
      +    polarity = "dark";
      +    opacity.popups = 0.5;
      +    cursor = {
      +      package = pkgs.capitaine-cursors;
      +      name = "capitaine-cursors";
      +      size = 16;
      +    };
      +    fonts = {
      +      sizes = {
      +        terminal = 10;
      +        applications = 11;
      +      };
      +      serif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      sansSerif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      monospace = {
      +        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
      +        name = "FiraCode Nerd Font Mono";
      +      };
      +
      +      emoji = {
      +        package = pkgs.noto-fonts-emoji;
      +        name = "Noto Color Emoji";
      +      };
      +    };
      +  };
      +
      +
      +
      +
      +  boot.loader.grub = {
      +    enable = true;
      +    device = "/dev/sda";
      +    useOSProber = true;
      +  };
      +
      +  networking.hostName = "onett"; # Define your hostname.
      +  networking.enableIPv6 = false;
      +
      +  users.users.swarsel = {
      +    isNormalUser = true;
      +    description = "Leon S";
      +    extraGroups = [ "networkmanager" "wheel" "lp"];
      +    packages = with pkgs; [];
      +  };
      +
      +  system.stateVersion = "23.05"; # Did you read the comment?
      +
      +  environment.systemPackages = with pkgs; [
      +  ];
      +
      +
      +}
      +
      +
      +
      +
      +
    2. + +
    3. Home Manager
      +
      +
      +
      +{ config, pkgs, lib, fetchFromGitHub, ... }:
      +
      +{
      +
      +
      +  services.gpg-agent = {
      +    enable = true;
      +    enableSshSupport = true;
      +    enableExtraSocket = true;
      +    pinentryPackage = pkgs.pinentry-gtk2;
      +    extraConfig = ''
      +    allow-emacs-pinentry
      +    allow-loopback-pinentry
      +    '';
      +    };
      +
      +
      +  home = {
      +    username = "swarsel";
      +    homeDirectory = "/home/swarsel";
      +    stateVersion = "23.05"; # Please read the comment before changing.
      +    keyboard.layout = "de";
      +    packages = with pkgs; [
      +    ];
      +  };
      +
      +  sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];
      +
      +  # # waybar config
      +  programs.waybar.settings.mainBar = {
      +    cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      +    temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon3/temp3_input";
      +  };
      +
      +  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
      +                                                    "mpris"
      +                                                    "custom/left-arrow-light"
      +                                                    "network"
      +                                                    "custom/left-arrow-dark"
      +                                                    "pulseaudio"
      +                                                    "custom/left-arrow-light"
      +                                                    "custom/pseudobat"
      +                                                    "battery"
      +                                                    "custom/left-arrow-dark"
      +                                                    "group/hardware"
      +                                                    "custom/left-arrow-light"
      +                                                    "clock#2"
      +                                                    "custom/left-arrow-dark"
      +                                                    "clock#1"
      +                                                   ];
      +
      +
      +  services.blueman-applet.enable = true;
      +
      +  wayland.windowManager.sway= {
      +    config = rec {
      +      input = {
      +        "1:1:AT_Translated_Set_2_keyboard" = {
      +          xkb_layout = "us";
      +          xkb_options = "grp:win_space_toggle";
      +          # xkb_options = "ctrl:nocaps,grp:win_space_toggle";
      +          xkb_variant = "altgr-intl";
      +        };
      +        "2362:33538:ipad_keyboard_Keyboard" = {
      +          xkb_layout = "us";
      +          xkb_options = "altwin:swap_lalt_lwin,ctrl:nocaps,grp:win_space_toggle";
      +          xkb_variant = "colemak_dh";
      +        };
      +        "36125:53060:splitkb.com_Kyria_rev3" = {
      +          xkb_layout = "us";
      +          xkb_variant = "altgr-intl";
      +        };
      +
      +        "type:touchpad" = {
      +          dwt = "enabled";
      +          tap = "enabled";
      +          natural_scroll = "enabled";
      +          middle_emulation = "enabled";
      +        };
      +      };
      +
      +      output = {
      +        eDP-1 = {
      +          mode = "1920x1080";
      +          scale = "1";
      +          bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
      +          position = "1920,0";
      +        };
      +        VGA-1 = {
      +          mode = "1920x1080";
      +          scale = "1";
      +          bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
      +          position = "0,0";
      +        };
      +      };
      +
      +      keybindings = let
      +        modifier = config.wayland.windowManager.sway.config.modifier;
      +      in {
      +        "${modifier}+F2"  = "exec brightnessctl set +5%";
      +        "${modifier}+F1"= "exec brightnessctl set 5%-";
      +        "XF86MonBrightnessUp"  = "exec brightnessctl set +5%";
      +        "XF86MonBrightnessDown"= "exec brightnessctl set 5%-";
      +        "${modifier}+Ctrl+p" = "exec wl-mirror eDP-1";
      +        "XF86HomePage" = "exec wtype -P Escape -p Escape";
      +        "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
      +      };
      +      keycodebindings = {
      +        "94" = "exec wtype c";
      +        "Shift+94" = "exec wtype C";
      +        "Ctrl+94" = "exec wtype -M ctrl c -m ctrl";
      +        "Ctrl+Shift+94" = "exec wtype -M ctrl -M shift c -m ctrl -m shift";
      +      };
      +
      +      startup = [
      +
      +        { command = "nextcloud --background";}
      +        { command = "discord --start-minimized";}
      +        { command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
      +        { command = "ANKI_WAYLAND=1 anki";}
      +        { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
      +        { command = "nm-applet";}
      +
      +      ];
      +    };
      +
      +    extraConfig = "
      + ";
      +  };
      +}
      +
      +
      +
      +
      +
    4. +
    +
  4. + +
  5. Threed (Surface Pro 3)
    +
    +

    +New setup for the SP3, this time using NixOS - another machine will take over the HM-only config for compatibility in the future. +

    +
    + +
      +
    1. NixOS
      +
      +
      +
      +{ config, lib, pkgs, inputs, ... }:
      +
      +{
      +
      +  imports =
      +    [
      +      ./hardware-configuration.nix
      +    ];
      +
      +
      +  services = {
      +    getty.autologinUser = "swarsel";
      +    greetd.settings.initial_session.user="swarsel";
      +  };
      +
      +  hardware.bluetooth.enable = true;
      +
      +  # Bootloader
      +  boot = {
      +    loader.systemd-boot.enable = lib.mkForce false;
      +    lanzaboote = {
      +      enable = true;
      +      pkiBundle = "/etc/secureboot";
      +    };
      +    loader.efi.canTouchEfiVariables = true;
      +    # use bootspec instead of lzbt for secure boot. This is not a generally needed setting
      +    bootspec.enable = true;
      +    # kernelPackages = pkgs.linuxPackages_latest;
      +  };
      +
      +  networking = {
      +    hostName = "threed";
      +    enableIPv6 = false;
      +    firewall.enable = false;
      +  };
      +
      +  stylix.image = ../../wallpaper/surfacewp.png;
      +
      +  stylix = {
      +    base16Scheme = ../../wallpaper/swarsel.yaml;
      +    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
      +    polarity = "dark";
      +    opacity.popups = 0.5;
      +    cursor = {
      +      package = pkgs.capitaine-cursors;
      +      name = "capitaine-cursors";
      +      size = 16;
      +    };
      +    fonts = {
      +      sizes = {
      +        terminal = 10;
      +        applications = 11;
      +      };
      +      serif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      sansSerif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      monospace = {
      +        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
      +        name = "FiraCode Nerd Font Mono";
      +      };
      +
      +      emoji = {
      +        package = pkgs.noto-fonts-emoji;
      +        name = "Noto Color Emoji";
      +      };
      +    };
      +  };
      +
      +
      +
      +
      +  users.users.swarsel = {
      +    isNormalUser = true;
      +    description = "Leon S";
      +    extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
      +    packages = with pkgs; [];
      +  };
      +
      +  environment.systemPackages = with pkgs; [
      +  ];
      +
      +  system.stateVersion = "23.05";
      +
      +}
      +
      +
      +
      +
      +
    2. + +
    3. Home Manager
      +
      +
      +
      +{ config, pkgs, lib, fetchFromGitHub, ... }:
      +
      +{
      +
      +
      +  services.gpg-agent = {
      +    enable = true;
      +    enableSshSupport = true;
      +    enableExtraSocket = true;
      +    pinentryPackage = pkgs.pinentry-gtk2;
      +    extraConfig = ''
      +    allow-emacs-pinentry
      +    allow-loopback-pinentry
      +    '';
      +    };
      +
      +
      +  home = {
      +    username = "swarsel";
      +    homeDirectory = "/home/swarsel";
      +    stateVersion = "23.05"; # Please read the comment before changing.
      +    keyboard.layout = "us";
      +    packages = with pkgs; [
      +    ];
      +  };
      +
      +  sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];
      +
      +  programs.waybar.settings.mainBar = {
      +    cpu.format = "{icon0} {icon1} {icon2} {icon3}";
      +    temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
      +  };
      +
      +  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
      +                                                    "mpris"
      +                                                    "custom/left-arrow-light"
      +                                                    "network"
      +                                                    "custom/left-arrow-dark"
      +                                                    "pulseaudio"
      +                                                    "custom/left-arrow-light"
      +                                                    "custom/pseudobat"
      +                                                    "battery"
      +                                                    "custom/left-arrow-dark"
      +                                                    "group/hardware"
      +                                                    "custom/left-arrow-light"
      +                                                    "clock#2"
      +                                                    "custom/left-arrow-dark"
      +                                                    "clock#1"
      +                                                   ];
      +
      +
      +  wayland.windowManager.sway= {
      +    config = rec {
      +      input = {
      +        "*" = {
      +          xkb_layout = "us";
      +          xkb_options = "grp:win_space_toggle";
      +          xkb_variant = "altgr-intl";
      +        };
      +        "type:touchpad" = {
      +          dwt = "enabled";
      +          tap = "enabled";
      +          natural_scroll = "enabled";
      +          middle_emulation = "enabled";
      +        };
      +      };
      +
      +      output = {
      +        eDP-1 = {
      +          mode = "2160x1440@59.955Hz";
      +          scale = "1";
      +          bg = "~/.dotfiles/wallpaper/surfacewp.png fill";
      +        };
      +      };
      +
      +      keybindings = let
      +        modifier = config.wayland.windowManager.sway.config.modifier;
      +      in {
      +        "${modifier}+F2"  = "exec brightnessctl set +5%";
      +        "${modifier}+F1"= "exec brightnessctl set 5%-";
      +        "${modifier}+n" = "exec sway output eDP-1 transform normal, splith";
      +        "${modifier}+Ctrl+p" = "exec wl-mirror eDP-1";
      +        "${modifier}+t" = "exec sway output eDP-1 transform 90, splitv";
      +        "${modifier}+XF86AudioLowerVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
      +        "${modifier}+XF86AudioRaiseVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
      +        "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
      +      };
      +
      +      startup = [
      +
      +        { command = "nextcloud --background";}
      +        { command = "discord --start-minimized";}
      +        { command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
      +        { command = "ANKI_WAYLAND=1 anki";}
      +        { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
      +        { command = "nm-applet";}
      +
      +      ];
      +
      +      keycodebindings = {
      +        "124" = "exec systemctl suspend";
      +      };
      +    };
      +
      +    extraConfig = "
      +    exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05 map_to_output eDP-1
      +    exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05_Stylus map_to_output eDP-1
      +    ";
      +  };
      +}
      +
      +
      +
      +
    4. +
    +
  6. + +
  7. Fourside (Lenovo Thinkpad P14s Gen2)
    +
    +

    +My new main machine. +

    +
    + +
      +
    1. NixOS
      +
      +

      +Mostly just sets some opened ports for several games, enables virtualbox (which I do not want everywhere because of resource considerations) and enables thinkfan, which allows for better fan control on Lenovo Thinkpad machines. +

      + +
      +
      +{ config, lib, pkgs, inputs, ... }:
      +
      +{
      +
      +  # 
      +  # imports =
      +  #   [
      +  #     ./hardware-configuration.nix
      +  #   ];
      +  # 
      +  imports =
      +    [
      +      ./hardware-configuration.nix
      +    ];
      +
      +  services = {
      +    getty.autologinUser = "swarsel";
      +    greetd.settings.initial_session.user="swarsel";
      +  };
      +
      +  boot = {
      +    loader.systemd-boot.enable = true;
      +    loader.efi.canTouchEfiVariables = true;
      +    # kernelPackages = pkgs.linuxPackages_latest;
      +  };
      +
      +  networking = {
      +    hostName = "fourside"; # Define your hostname.
      +    nftables.enable = true;
      +    enableIPv6 = false;
      +    firewall.checkReversePath = false;
      +    firewall = {
      +      enable = true;
      +      allowedUDPPorts = [ 4380 27036 14242 34197 51820 ]; # 34197: factorio; 4380 27036 14242: barotrauma; 51820: wireguard
      +      allowedTCPPortRanges = [
      +        {from = 27015; to = 27030;} # barotrauma
      +        {from = 27036; to = 27037;} # barotrauma
      +      ];
      +      allowedUDPPortRanges = [
      +        {from = 27000; to = 27031;} # barotrauma
      +        {from = 58962; to = 58964;} # barotrauma
      +      ];
      +    };
      +  };
      +
      +  virtualisation.virtualbox = {
      +    host = {
      +    enable = true;
      +    enableExtensionPack = true;
      +    };
      +    guest = {
      +      enable = true;
      +      };
      +    };
      +
      +  stylix.image = ../../wallpaper/lenovowp.png;
      +
      +  stylix = {
      +    base16Scheme = ../../wallpaper/swarsel.yaml;
      +    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
      +    polarity = "dark";
      +    opacity.popups = 0.5;
      +    cursor = {
      +      package = pkgs.capitaine-cursors;
      +      name = "capitaine-cursors";
      +      size = 16;
      +    };
      +    fonts = {
      +      sizes = {
      +        terminal = 10;
      +        applications = 11;
      +      };
      +      serif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      sansSerif = {
      +        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
      +        package = pkgs.cantarell-fonts;
      +        # package = pkgs.montserrat;
      +        name = "Cantarell";
      +        # name = "FiraCode Nerd Font Propo";
      +        # name = "Montserrat";
      +      };
      +
      +      monospace = {
      +        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
      +        name = "FiraCode Nerd Font Mono";
      +      };
      +
      +      emoji = {
      +        package = pkgs.noto-fonts-emoji;
      +        name = "Noto Color Emoji";
      +      };
      +    };
      +  };
      +
      +
      +
      +
      +  hardware = {
      +      opengl = {
      +        enable = true;
      +        driSupport = true;
      +        driSupport32Bit = true;
      +        extraPackages = with pkgs; [
      +          vulkan-loader
      +          vulkan-validation-layers
      +          vulkan-extension-layer
      +        ];
      +      };
      +      bluetooth.enable = true;
      +    };
      +
      +  programs.steam = {
      +    enable = true;
      +    extraCompatPackages = [
      +      pkgs.proton-ge-bin
      +    ];
      +  };
      +
      +    # Configure keymap in X11 (only used for login)
      +
      +  services.thinkfan = {
      +    enable = false;
      +  };
      +  services.power-profiles-daemon.enable = true;
      +
      +  users.users.swarsel = {
      +    isNormalUser = true;
      +    description = "Leon S";
      +    extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" "vboxusers" "scanner" ];
      +    packages = with pkgs; [];
      +  };
      +
      +  environment.systemPackages = with pkgs; [
      +      # gog games installing
      +      heroic
      +      # minecraft
      +      temurin-bin-17
      +      (prismlauncher.override {
      +        glfw = pkgs.glfw-wayland-minecraft;
      +      })
      +  ];
      +
      +  system.stateVersion = "23.05";
      +
      +
      +}
      +
      +
      +
      +
      +
    2. + +
    3. Home Manager
      +
      +

      +This is basically just adjusted to the core count, path to the hwmon (this was very bothersome on this machine due to changing address), as well as making use of the top-row function keys. +

      + +
      +
      +{ config, pkgs, lib, fetchFromGitHub, ... }:
      +
      +{
      +
      +
      +  services.gpg-agent = {
      +    enable = true;
      +    enableSshSupport = true;
      +    enableExtraSocket = true;
      +    pinentryPackage = pkgs.pinentry-gtk2;
      +    extraConfig = ''
      +    allow-emacs-pinentry
      +    allow-loopback-pinentry
      +    '';
      +    };
      +
      +  home = {
      +    username = "swarsel";
      +    homeDirectory = "/home/swarsel";
      +    stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing.
      +    keyboard.layout = "us"; # TEMPLATE
      +    packages = with pkgs; [
      +    ];
      +  };
      +  sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];
      +
      +  # waybar config - TEMPLATE - update for cores and temp
      +  programs.waybar.settings.mainBar = {
      +    cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      +    # temperature.hwmon-path = "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon4/temp1_input";
      +    temperature.hwmon-path.abs = "/sys/devices/platform/thinkpad_hwmon/hwmon/";
      +    temperature.input-filename = "temp1_input";
      +  };
      +
      +
      +  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
      +                                                    "mpris"
      +                                                    "custom/left-arrow-light"
      +                                                    "network"
      +                                                    "custom/left-arrow-dark"
      +                                                    "pulseaudio"
      +                                                    "custom/left-arrow-light"
      +                                                    "custom/pseudobat"
      +                                                    "battery"
      +                                                    "custom/left-arrow-dark"
      +                                                    "group/hardware"
      +                                                    "custom/left-arrow-light"
      +                                                    "clock#2"
      +                                                    "custom/left-arrow-dark"
      +                                                    "clock#1"
      +                                                   ];
      +
      +
      +  wayland.windowManager.sway= {
      +    config = rec {
      +      # update for actual inputs here,
      +      input = {
      +        "36125:53060:splitkb.com_Kyria_rev3" = {
      +          xkb_layout = "us";
      +          xkb_variant = "altgr-intl";
      +        };
      +        "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE
      +          xkb_layout = "us";
      +          xkb_options = "grp:win_space_toggle";
      +          xkb_variant = "altgr-intl";
      +        };
      +        "type:touchpad" = {
      +          dwt = "enabled";
      +          tap = "enabled";
      +          natural_scroll = "enabled";
      +          middle_emulation = "enabled";
      +        };
      +
      +      };
      +
      +      output = {
      +        DP-1 = {
      +          mode = "2560x1440"; # TEMPLATE
      +          scale = "1";
      +          #bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
      +        };
      +      };
      +
      +      keybindings = let
      +        modifier = config.wayland.windowManager.sway.config.modifier;
      +      in {
      +        "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\"";
      +        "XF86MonBrightnessUp"  = "exec brightnessctl set +5%";
      +        "XF86MonBrightnessDown"= "exec brightnessctl set 5%-";
      +        "XF86Display" = "exec wl-mirror eDP-1";
      +        # these are left open to use
      +        # "XF86WLAN" = "exec wl-mirror eDP-1";
      +        # "XF86Messenger" = "exec wl-mirror eDP-1";
      +        # "XF86Go" = "exec wl-mirror eDP-1";
      +        # "XF86Favorites" = "exec wl-mirror eDP-1";
      +        # "XF86HomePage" = "exec wtype -P Escape -p Escape";
      +        # "XF86AudioLowerVolume" = "pactl set-sink-volume alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink -5%";
      +        # "XF86AudioRaiseVolume" = "pactl set-sink-volume alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink +5%  ";
      +        "XF86AudioMute" = "pactl set-sink-mute alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink toggle";
      +      };
      +
      +      startup = [
      +
      +        { command = "nextcloud --background";}
      +        { command = "discord --start-minimized";}
      +        { command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
      +        { command = "ANKI_WAYLAND=1 anki";}
      +        { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
      +        { command = "nm-applet";}
      +
      +      ];
      +    };
      +  };
      +}
      +
      +
      +
      +
      +
    4. +
    +
  8. +
+
+ +
+

3.1.3. Virtual hosts

+
+

+My server setup is built on Proxmox VE; back when I started, I created all kinds of wild Debian/Ubuntu/etc. KVMs and LXCs on there. However, the root disk has suffered a weird failure where it has become unable to be cloned, but it is still functional for now. I am currently rewriting all machines on there to use NixOS instead; this is a ongoing process. +

+ +

+In the long run, I am thinking about a transition to kubernetes or using just a server running NixOS and using the built-in container functionality. For now however, I like the network management provided by Proxmox, as I am a bit intimidated by doing that from scratch. +

+
+ +
    +
  1. TEMPLATE
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ pkgs, modulesPath, ... }:
      +
      +{
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +  ];
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.hostName = "TEMPLATE"; # Define your hostname.
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  networking.firewall.enable = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +  # users.users.root.password = "TEMPLATE";
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +}
      +
      +
      +
      +
      +
    2. +
    +
  2. + +
  3. NGINX
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +{
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +    lego
      +    nginx
      +  ];
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/.dotfiles/secrets/nginx/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.dnstokenfull = {owner="acme";};
      +  sops.templates."certs.secret".content = ''
      +  CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
      +  '';
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.hostName = "nginx"; # Define your hostname.
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  networking.firewall.enable = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +  # users.users.root.password = "TEMPLATE";
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  security.acme = {
      +    acceptTerms = true;
      +    preliminarySelfsigned = false;
      +    defaults.email = "mrswarsel@gmail.com";
      +    defaults.dnsProvider = "cloudflare";
      +    defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
      +  };
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +  services.nginx = {
      +    enable = true;
      +    recommendedProxySettings = true;
      +    recommendedTlsSettings = true;
      +    recommendedOptimisation = true;
      +    recommendedGzipSettings = true;
      +    virtualHosts = {
      +
      +      "stash.swarsel.win" = {
      +        enableACME = true;
      +        forceSSL = true;
      +        acmeRoot = null;
      +        locations = {
      +          "/" = {
      +            proxyPass = "https://192.168.1.5";
      +            extraConfig = ''
      +            client_max_body_size 0;
      +            '';
      +          };
      +          # "/push/" = {
      +            # proxyPass = "http://192.168.2.5:7867";
      +          # };
      +          "/.well-known/carddav" = {
      +            return = "301 $scheme://$host/remote.php/dav";
      +          };
      +          "/.well-known/caldav" = {
      +            return = "301 $scheme://$host/remote.php/dav";
      +          };
      +        };
      +      };
      +
      +      "matrix2.swarsel.win" = {
      +        enableACME = true;
      +        forceSSL = true;
      +        acmeRoot = null;
      +        locations = {
      +          "~ ^(/_matrix|/_synapse/client)" = {
      +            proxyPass = "http://192.168.1.23:8008";
      +            extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +          };
      +        };
      +      };
      +
      +
      +        "sound.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://192.168.1.13:4040";
      +              proxyWebsockets = true;
      +              extraConfig = ''
      +                proxy_redirect          http:// https://;
      +                proxy_read_timeout      600s;
      +                proxy_send_timeout      600s;
      +                proxy_buffering         off;
      +                proxy_request_buffering off;
      +                client_max_body_size    0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "scan.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://192.168.1.24:28981";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "screen.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://192.168.1.16:8096";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "matrix.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "~ ^(/_matrix|/_synapse/client)" = {
      +              proxyPass = "http://192.168.1.20:8008";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "scroll.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://192.168.1.22:8080";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "blog.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "https://192.168.1.7";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +      };
      +    };
      +
      +
      +
      +
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  4. + +
  5. [Manual steps required] Calibre
    +
    +

    +This machine requires manual setup: +

    +
      +
    1. (obsolete for now) Set up calibre-web: +
        +
      • Create metadata.db with 664 permissions, make sure parent directory is writeable
      • +
      • Login @ books.swarsel.win using initial creds: +
          +
        • user: admin
        • +
        • pw: admin123
        • +
      • +
      • point to metadata.db file, make sure you can upload
      • +
      • Change pw, create normal user
      • +
    2. +
    3. Setup kavita: +
        +
      • Login @ scrolls.swarsel.win
      • +
      • Create admin user
      • +
      • Import Libraries
      • +
      • Create normal user
      • +
    4. +
    + +

    +In general, I am not amazed by this setup; Kavita is the reader of choice, calibre-web mostly is there to have a convenient way to fullfill the opinionated folder structure when uploading ebooks (calibre-web does not work on its own since it forces sqlite which does not work nicely with my NFS book store). I hope that in the future Kavita will implement ebook upload, or that calibre-web will ditch the sqlite constraints. +

    +
    + +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +    calibre
      +  ];
      +
      +  users.groups.lxc_shares = {
      +    gid = 10000;
      +    members = [
      +            "kavita"
      +            "calibre-web"
      +            "root"
      +          ];
      +  };
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/.dotfiles/secrets/calibre/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.kavita = { owner = "kavita";};
      +  # sops.secrets.smbuser = { };
      +  # sops.secrets.smbpassword = { };
      +  # sops.secrets.smbdomain = { };
      +  # sops.templates."smb.cred".content = ''
      +  # user=${config.sops.placeholder.smbuser}
      +  # password=${config.sops.placeholder.smbpassword}
      +  # domain=${config.sops.placeholder.smbdomain}
      +  # '';
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.hostName = "calibre"; # Define your hostname.
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  networking.firewall.enable = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +
      +    # services.calibre-server = {
      +    # enable = true;
      +    # user = "calibre-server";
      +    # auth.enable = true;
      +    # auth.userDb = "/srv/calibre/users.sqlite";
      +    # libraries = [
      +    #   /media/Books/main
      +    #   /media/Books/diverse
      +    #   /media/Books/language
      +    #   /media/Books/science
      +    #   /media/Books/sport
      +    #   /media/Books/novels
      +    # ];
      +  # };
      +
      +  # services.calibre-web = {
      +  #   enable = true;
      +  #   user = "calibre-web";
      +  #   group = "calibre-web";
      +  #   listen.port = 8083;
      +  #   listen.ip = "0.0.0.0";
      +  #   options = {
      +  #     enableBookUploading = true;
      +  #     enableKepubify = true;
      +  #     enableBookConversion = true;
      +  #   };
      +  # };
      +
      +  services.kavita = {
      +    enable = true;
      +    user = "kavita";
      +    port = 8080;
      +    tokenKeyFile = config.sops.secrets.kavita.path;
      +  };
      +
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  6. + +
  7. Jellyfin
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +  ];
      +
      +  users.groups.lxc_shares = {
      +    gid = 10000;
      +    members = [
      +      "jellyfin"
      +      "root"
      +    ];
      +  };
      +
      +  users.users.jellyfin = {
      +    extraGroups  = [ "video" "render" ];
      +  };
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  # sops.defaultSopsFile = "/.dotfiles/secrets/jellyfin/secrets.yaml";
      +  # sops.validateSopsFiles = false;
      +
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.hostName = "jellyfin"; # Define your hostname.
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  networking.firewall.enable = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +   nixpkgs.config.packageOverrides = pkgs: {
      +     vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
      +   };
      +   hardware.opengl = {
      +     enable = true;
      +     extraPackages = with pkgs; [
      +       intel-media-driver # LIBVA_DRIVER_NAME=iHD
      +       vaapiIntel         # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
      +       vaapiVdpau
      +       libvdpau-va-gl
      +     ];
      +   };
      +
      +  services.jellyfin = {
      +    enable = true;
      +    user = "jellyfin";
      +    # openFirewall = true; # this works only for the default ports
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  8. + +
  9. [WIP/Incomplete/Untested] Transmission
    +
    +

    +This stuff just does not work, I seem to be unable to create a working VPN Split Tunneling on NixOS. Maybe this is introduced by the wonky Proxmox-NixOS container interaction, I am not sure. For now, this machine does not work at all and I am stuck with my Debian Container that does this for me … +

    +
    + +
      +
    1. NixOS
      +
      +
      +
      +            { config, pkgs, modulesPath, ... }:
      +
      +            {
      +              imports = [
      +                (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +                ./hardware-configuration.nix
      +                # ./openvpn.nix #this file holds the vpn login data
      +              ];
      +
      +              environment.systemPackages = with pkgs; [
      +                git
      +                gnupg
      +                ssh-to-age
      +                openvpn
      +                jq
      +                iptables
      +                busybox
      +                wireguard-tools
      +              ];
      +
      +              users.groups.lxc_shares = {
      +                gid = 10000;
      +                members = [
      +                  "vpn"
      +                  "radarr"
      +                  "sonarr"
      +                  "lidarr"
      +                  "readarr"
      +                  "root"
      +                ];
      +              };
      +              users.groups.vpn = {};
      +
      +              users.users.vpn = {
      +                isNormalUser = true;
      +                group = "vpn";
      +                home = "/home/vpn";
      +              };
      +
      +              services.xserver = {
      +                layout = "us";
      +                xkbVariant = "altgr-intl";
      +              };
      +
      +              nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +              sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +              sops.defaultSopsFile = "/.dotfiles/secrets/transmission/secrets.yaml";
      +              sops.validateSopsFiles = false;
      +
      +              boot.kernelModules = [ "tun" ];
      +              proxmoxLXC.manageNetwork = true; # manage network myself
      +              proxmoxLXC.manageHostName = false; # manage hostname myself
      +              networking.hostName = "transmission"; # Define your hostname.
      +              networking.useDHCP = true;
      +              networking.enableIPv6 = false;
      +              networking.firewall.enable = false;
      +
      +              services.radarr = {
      +                enable = true;
      +              };
      +
      +              services.readarr = {
      +                enable = true;
      +              };
      +              services.sonarr = {
      +                enable = true;
      +              };
      +              services.lidarr = {
      +                enable = true;
      +              };
      +              services.prowlarr = {
      +                enable = true;
      +              };
      +
      +              # networking.interfaces = {
      +                  # lo = {
      +                    # useDHCP = false;
      +                    # ipv4.addresses = [
      +                      # { address = "127.0.0.1"; prefixLength = 8; }
      +                    # ];
      +                  # };
      +              #
      +                  # eth0 = {
      +                    # useDHCP = true;
      +                  # };
      +                # };
      +
      +              # networking.firewall.extraCommands = ''
      +              # sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP
      +              # '';
      +              networking.iproute2 = {
      +                enable = true;
      +                rttablesExtraConfig = ''
      +                200     vpn
      +                '';
      +              };
      +              # boot.kernel.sysctl = {
      +              #   "net.ipv4.conf.all.rp_filter" = 2;
      +              #   "net.ipv4.conf.default.rp_filter" = 2;
      +              #   "net.ipv4.conf.eth0.rp_filter" = 2;
      +              # };
      +              environment.etc = {
      +                "openvpn/iptables.sh" =
      +                  { source = ../../../scripts/server1/iptables.sh;
      +                    mode = "0755";
      +                  };
      +                "openvpn/update-resolv-conf" =
      +                  { source = ../../../scripts/server1/update-resolv-conf;
      +                    mode = "0755";
      +                  };
      +                "openvpn/routing.sh" =
      +                  { source = ../../../scripts/server1/routing.sh;
      +                    mode = "0755";
      +                  };
      +                "openvpn/ca.rsa.2048.crt" =
      +                  { source = ../../../secrets/certs/ca.rsa.2048.crt;
      +                    mode = "0644";
      +                  };
      +                "openvpn/crl.rsa.2048.pem" =
      +                  { source = ../../../secrets/certs/crl.rsa.2048.pem;
      +                    mode = "0644";
      +                  };
      +              };
      +              services.openssh = {
      +                enable = true;
      +                settings.PermitRootLogin = "yes";
      +                listenAddresses = [{
      +                                   port = 22;
      +                                   addr = "0.0.0.0";
      +                                 }];
      +              };
      +              users.users.root.openssh.authorizedKeys.keyFiles = [
      +                ../../../secrets/keys/authorized_keys
      +              ];
      +
      +              system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +              # users.users.root.password = "TEMPLATE";
      +
      +              environment.shellAliases = {
      +                nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +              };
      +
      +              sops.secrets.vpnuser = {};
      +              sops.secrets.rpcuser = {owner="vpn";};
      +              sops.secrets.vpnpass = {};
      +              sops.secrets.rpcpass = {owner="vpn";};
      +              sops.secrets.vpnprot = {};
      +              sops.secrets.vpnloc = {};
      +              # sops.secrets.crlpem = {};
      +              # sops.secrets.capem = {};
      +              sops.templates."transmission-rpc".owner = "vpn";
      +              sops.templates."transmission-rpc".content = builtins.toJSON {
      +                rpc-username = config.sops.placeholder.rpcuser;
      +                rpc-password = config.sops.placeholder.rpcpass;
      +              };
      +
      +              sops.templates.pia.content = ''
      +              ${config.sops.placeholder.vpnuser}
      +              ${config.sops.placeholder.vpnpass}
      +              '';
      +
      +              sops.templates.vpn.content = ''
      +                client
      +                dev tun
      +                proto ${config.sops.placeholder.vpnprot}
      +                remote ${config.sops.placeholder.vpnloc}
      +                resolv-retry infinite
      +                nobind
      +                persist-key
      +                persist-tun
      +                cipher aes-128-cbc
      +                auth sha1
      +                tls-client
      +                remote-cert-tls server
      +
      +                auth-user-pass ${config.sops.templates.pia.path}
      +                compress
      +                verb 1
      +                reneg-sec 0
      +
      +                crl-verify /etc/openvpn/crl.rsa.2048.pem
      +                ca /etc/openvpn/ca.rsa.2048.crt
      +
      +                disable-occ
      +                dhcp-option DNS 209.222.18.222
      +                dhcp-option DNS 209.222.18.218
      +                dhcp-option DNS 8.8.8.8
      +                route-noexec
      +              '';
      +
      +              # services.pia.enable = true;
      +              # services.pia.authUserPass.username = "na";
      +              # services.pia.authUserPass.password = "na";
      +
      +
      +            #     systemd.services.openvpn-vpn = {
      +            # wantedBy = [ "multi-user.target" ];
      +            # after = [ "network.target" ];
      +            # description = "OpenVPN connection to pia";
      +            # serviceConfig = {
      +            #   Type = "forking";
      +            #   RuntimeDirectory="openvpn";
      +            #   PrivateTmp=true;
      +            #   KillMode="mixed";
      +            #   ExecStart = ''@${pkgs.openvpn}/sbin/openvpn openvpn --daemon ovpn-pia --status /run/openvpn/pia.status 10 --cd /etc/openvpn --script-security 2 --config ${config.sops.templates.vpn.path} --writepid /run/openvpn/pia.pid'';
      +            #   PIDFile=''/run/openvpn/pia.pid'';
      +            #   ExecReload=''/run/current-system/sw/bin/kill -HUP $MAINPID'';
      +            #   WorkingDirectory="/etc/openvpn";
      +            #   Restart="on-failure";
      +            #   RestartSec=30;
      +            #   ProtectSystem="yes";
      +            #   DeviceAllow=["/dev/null rw" "/dev/net/tun rw"];
      +            # };
      +         # };
      +          services.openvpn.servers = {
      +            pia = {
      +              autoStart = false;
      +              updateResolvConf = true;
      +#               up = ''
      +# export INTERFACE="tun0"
      +# export VPNUSER="vpn"
      +# export LOCALIP="192.168.1.191"
      +# export NETIF="eth0"
      +# export VPNIF="tun0"
      +# export GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1)
      +# iptables -F -t nat
      +# iptables -F -t mangle
      +# iptables -F -t filter
      +# iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark
      +# iptables -t mangle -A OUTPUT ! --dest $LOCALIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
      +# iptables -t mangle -A OUTPUT --dest $LOCALIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
      +# iptables -t mangle -A OUTPUT --dest $LOCALIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
      +# iptables -t mangle -A OUTPUT ! --src $LOCALIP -j MARK --set-mark 0x1
      +# iptables -t mangle -A OUTPUT -j CONNMARK --save-mark
      +# iptables -A INPUT -i $INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
      +# iptables -A INPUT -i $INTERFACE -j REJECT
      +# iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT
      +# iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT
      +# iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE
      +# iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
      +# iptables -A OUTPUT ! --src $LOCALIP -o $NETIF -j REJECT
      +# if [[ `ip rule list | grep -c 0x1` == 0 ]]; then
      +# ip rule add from all fwmark 0x1 lookup $VPNUSER
      +# fi
      +# ip route replace default via $GATEWAYIP table $VPNUSER
      +# ip route append default via 127.0.0.1 dev lo table $VPNUSER
      +# ip route flush cache
      +              # '';
      +              # down = "bash /etc/openvpn/update-resolv-conf";
      +              # these are outsourced to a local file, I am not sure if it can be done with sops-nix
      +              # authUserPass = {
      +                # username = "TODO:secrets";
      +                # password = "TODO:secrets";
      +              # };
      +              config = "config ${config.sops.templates.vpn.path}";
      +            };
      +          };
      +
      +        services.transmission = {
      +          enable = true;
      +          credentialsFile = config.sops.templates."transmission-rpc".path;
      +          user = "vpn";
      +          group = "lxc_shares";
      +          settings = {
      +
      +          alt-speed-down= 8000;
      +          alt-speed-enabled= false;
      +          alt-speed-time-begin= 0;
      +          alt-speed-time-day= 127;
      +          alt-speed-time-enabled= true;
      +          alt-speed-time-end= 360;
      +          alt-speed-up= 2000;
      +          bind-address-ipv4= "0.0.0.0";
      +          bind-address-ipv6= "::";
      +          blocklist-enabled= false;
      +          blocklist-url= "http://www.example.com/blocklist";
      +          cache-size-mb= 4;
      +          dht-enabled= false;
      +          download-dir= "/media/Eternor/New";
      +          download-limit= 100;
      +          download-limit-enabled= 0;
      +          download-queue-enabled= true;
      +          download-queue-size= 5;
      +          encryption= 2;
      +          idle-seeding-limit= 30;
      +          idle-seeding-limit-enabled= false;
      +          incomplete-dir= "/var/lib/transmission-daemon/Downloads";
      +          incomplete-dir-enabled= false;
      +          lpd-enabled= false;
      +          max-peers-global= 200;
      +          message-level= 1;
      +          peer-congestion-algorithm= "";
      +          peer-id-ttl-hours= 6;
      +          peer-limit-global= 100;
      +          peer-limit-per-torrent= 40;
      +          peer-port= 22371;
      +          peer-port-random-high= 65535;
      +          peer-port-random-low= 49152;
      +          peer-port-random-on-start= false;
      +          peer-socket-tos= "default";
      +          pex-enabled= false;
      +          port-forwarding-enabled= false;
      +          preallocation= 1;
      +          prefetch-enabled= true;
      +          queue-stalled-enabled= true;
      +          queue-stalled-minutes= 30;
      +          ratio-limit= 2;
      +          ratio-limit-enabled= false;
      +          rename-partial-files= true;
      +          rpc-authentication-required= true;
      +          rpc-bind-address= "0.0.0.0";
      +          rpc-enabled= true;
      +          rpc-host-whitelist= "";
      +          rpc-host-whitelist-enabled= true;
      +          rpc-port= 9091;
      +          rpc-url= "/transmission/";
      +          rpc-whitelist= "127.0.0.1,192.168.3.2";
      +          rpc-whitelist-enabled= true;
      +          scrape-paused-torrents-enabled= true;
      +          script-torrent-done-enabled= false;
      +          seed-queue-enabled= false;
      +          seed-queue-size= 10;
      +          speed-limit-down= 6000;
      +          speed-limit-down-enabled= true;
      +          speed-limit-up= 500;
      +          speed-limit-up-enabled= true;
      +          start-added-torrents= true;
      +          trash-original-torrent-files= false;
      +          umask= 2;
      +          upload-limit= 100;
      +          upload-limit-enabled= 0;
      +          upload-slots-per-torrent= 14;
      +          utp-enabled= false;
      +          };
      +        };
      +
      +      # services.nginx = {
      +      #       enable = true;
      +      #       virtualHosts = {
      +
      +      #         "192.168.1.192" = {
      +      #           locations = {
      +      #             "/transmission" = {
      +      #               proxyPass = "http://127.0.0.1:9091";
      +      #               extraConfig = ''
      +      #               proxy_set_header Host $host;
      +      #               proxy_set_header X-Real-IP $remote_addr;
      +      #               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      +      #               '';
      +      #             };
      +      #           };
      +      #         };
      +      #       };
      +      # };
      +
      +
      +            }
      +
      +
      +
      +
      +
    2. +
    +
  10. + +
  11. [Manual steps needed] Matrix
    +
    +
      +
    1. After the initial setup, run the +
        +
      • /run/secrets-generated/matrixuserregister.sh
      • +
    2. +
    +

    +command to register a new admin user. +

    +
      +
    1. All bridges will fail on first start, copy the registration files using: +
        +
      • cp var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse
      • +
      • chown matrix-synapse:matrix-synapse var/lib/matrix-synapse/telegram-registration.yaml
      • +
    2. +
    +

    +Make sure to also do this for doublepuppet.yaml +

    +
      +
    1. Restart postgresql.service, matrix-synapse.service, mautrix-whatsapp.service, mautrix-telegram.service
    2. +
    +
    + +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, unstable, sops, ... }: let
      +  matrixDomain = "matrix2.swarsel.win";
      +in {
      +
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +    listenAddresses = [{
      +      port = 22;
      +      addr = "0.0.0.0";
      +    }];
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +    # we import here a service that is not available yet on normal nixpkgs
      +    # this module is hence not in the modules list, we add it ourselves
      +    (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix")
      +  ];
      +
      +  networking.hostName = "matrix"; # Define your hostname.
      +  networking.firewall.enable = false;
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +    matrix-synapse
      +    lottieconverter
      +    ffmpeg
      +  ];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/.dotfiles/secrets/matrix/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
      +  sops.templates."matrix_user_register.sh".content = ''
      +  register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
      +  '';
      +  sops.templates.matrixshared.owner = "matrix-synapse";
      +  sops.templates.matrixshared.content = ''
      +  registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
      +  '';
      +  sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
      +  sops.templates.mautrixtelegram.owner = "matrix-synapse";
      +  sops.templates.mautrixtelegram.content = ''
      +  MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
      +  MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
      +  MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
      +  MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
      +  '';
      +  # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
      +  # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
      +  # sops.templates.mautrixwhatsapp.content = ''
      +  # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
      +  # '';
      +
      +  services.postgresql.enable = true;
      +  services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
      +    CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
      +    CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
      +    CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
      +    CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
      +    CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +  '';
      +
      +  services.matrix-synapse = {
      +    settings.app_service_config_files = [
      +      "/var/lib/matrix-synapse/telegram-registration.yaml"
      +      "/var/lib/matrix-synapse/whatsapp-registration.yaml"
      +      "/var/lib/matrix-synapse/signal-registration.yaml"
      +      "/var/lib/matrix-synapse/doublepuppet.yaml"
      +    ];
      +    enable = true;
      +    settings.server_name = matrixDomain;
      +    settings.public_baseurl = "https://${matrixDomain}";
      +    extraConfigFiles = [
      +      config.sops.templates.matrixshared.path
      +    ];
      +    settings.listeners = [
      +      { port = 8008;
      +        bind_addresses = [ "0.0.0.0" ];
      +        type = "http";
      +        tls = false;
      +        x_forwarded = true;
      +        resources = [
      +          {
      +            names = [ "client" "federation" ];
      +            compress = true;
      +          }
      +        ];
      +      }
      +    ];
      +  };
      +
      +  services.mautrix-telegram = {
      +    enable = true;
      +    environmentFile = config.sops.templates.mautrixtelegram.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +        address= "http://localhost:29317";
      +        hostname = "0.0.0.0";
      +        port = "29317";
      +        provisioning.enabled = true;
      +        id = "telegram";
      +        # ephemeral_events = true; # not needed due to double puppeting
      +        public = {
      +          enabled = false;
      +        };
      +        database = "postgresql:///mautrix-telegram?host=/run/postgresql";
      +      };
      +      bridge = {
      +        # login_shared_secret_map = {
      +          # matrixDomain = "as_token:doublepuppet";
      +        # };
      +        relaybot.authless_portals = true;
      +        allow_avatar_remove = true;
      +        allow_contact_info = true;
      +        sync_channel_members = true;
      +        startup_sync = true;
      +        sync_create_limit = 0;
      +        sync_direct_chats = true;
      +        telegram_link_preview = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +        # Animated stickers conversion requires additional packages in the
      +        # service's path.
      +        # If this isn't a fresh installation, clearing the bridge's uploaded
      +        # file cache might be necessary (make a database backup first!):
      +        # delete from telegram_file where \
      +        #   mime_type in ('application/gzip', 'application/octet-stream')
      +        animated_sticker = {
      +          target = "gif";
      +          args = {
      +            width = 256;
      +            height = 256;
      +            fps = 30;               # only for webm
      +            background = "020202";  # only for gif, transparency not supported
      +          };
      +        };
      +      };
      +    };
      +  };
      +  systemd.services.mautrix-telegram.path = with pkgs; [
      +    lottieconverter  # for animated stickers conversion, unfree package
      +    ffmpeg           # if converting animated stickers to webm (very slow!)
      +  ];
      +
      +  services.mautrix-whatsapp = {
      +    enable = true;
      +    # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +        address= "http://localhost:29318";
      +        hostname = "0.0.0.0";
      +        port = 29318;
      +        database = {
      +          type = "postgres";
      +          uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
      +        };
      +      };
      +      bridge = {
      +        displayname_template = "{{or .FullName .PushName .JID}} (WA)";
      +        history_sync = {
      +          backfill = true;
      +          max_initial_conversations = -1;
      +          message_count = -1;
      +          request_full_sync = true;
      +          full_sync_config = {
      +            days_limit = 900;
      +            size_mb_limit = 5000;
      +            storage_quota_mb = 5000;
      +          };
      +        };
      +        login_shared_secret_map = {
      +          matrixDomain = "as_token:doublepuppet";
      +        };
      +        sync_manual_marked_unread = true;
      +        send_presence_on_typing = true;
      +        parallel_member_sync = true;
      +        url_previews = true;
      +        caption_in_message = true;
      +        extev_polls = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +      };
      +    };
      +  };
      +
      +  services.mautrix-signal = {
      +    enable = true;
      +    # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +
      +        address= "http://localhost:29328";
      +        hostname = "0.0.0.0";
      +        port = 29328;
      +        database = {
      +          type = "postgres";
      +          uri = "postgresql:///mautrix-signal?host=/run/postgresql";
      +        };
      +      };
      +      bridge = {
      +        displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
      +        login_shared_secret_map = {
      +          matrixDomain = "as_token:doublepuppet";
      +        };
      +        caption_in_message = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +      };
      +    };
      +  };
      +
      +  # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
      +  # messages out after a while.
      +
      +  systemd.timers."restart-bridges" = {
      +    wantedBy = [ "timers.target" ];
      +    timerConfig = {
      +      OnBootSec = "1d";
      +      OnUnitActiveSec = "1d";
      +      Unit = "restart-bridges.service";
      +    };
      +  };
      +
      +  systemd.services."restart-bridges" = {
      +    script = ''
      +    systemctl restart mautrix-whatsapp.service
      +    systemctl restart mautrix-signal.service
      +    systemctl restart mautrix-telegram.service
      +    '';
      +    serviceConfig = {
      +      Type = "oneshot";
      +      User = "root";
      +    };
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  12. + +
  13. Sound
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +    listenAddresses = [{
      +      port = 22;
      +      addr = "0.0.0.0";
      +    }];
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +
      +
      +  proxmoxLXC.privileged = true; # manage hostname myself
      +
      +  users.groups.lxc_pshares = {
      +    gid = 110000;
      +    members = [
      +      "navidrome"
      +      "mpd"
      +      "root"
      +    ];
      +  };
      +
      +  users.groups.navidrome = {
      +    gid = 61593;
      +  };
      +
      +  users.groups.mpd = {};
      +
      +  users.users.navidrome = {
      +    isSystemUser = true;
      +    uid = 61593;
      +    group = "navidrome";
      +    extraGroups  = [ "audio" "utmp" ];
      +  };
      +
      +  users.users.mpd = {
      +    isSystemUser = true;
      +    group = "mpd";
      +    extraGroups  = [ "audio" "utmp" ];
      +  };
      +
      +  sound = {
      +    enable = true;
      +  };
      +
      +  hardware.enableAllFirmware = true;
      +  networking.hostName = "sound"; # Define your hostname.
      +  networking.firewall.enable = false;
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +    pciutils
      +    alsa-utils
      +    mpv
      +  ];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/.dotfiles/secrets/sound/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.mpdpass = { owner = "mpd";};
      +
      +  services.navidrome = {
      +    enable = true;
      +    settings = {
      +      Address = "0.0.0.0";
      +      Port = 4040;
      +      MusicFolder = "/media";
      +      EnableSharing = true;
      +      EnableTranscodingConfig = true;
      +      Scanner.GroupAlbumReleases = true;
      +      ScanSchedule = "@every 1d";
      +      # Insert these values locally as sops-nix does not work for them
      +      LastFM.ApiKey = TEMPLATE;
      +      LastFM.Secret = TEMPLATE;
      +      Spotify.ID = TEMPLATE;
      +      Spotify.Secret = TEMPLATE;
      +      UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png";
      +      UIWelcomeMessage = "~SwarselSound~";
      +    };
      +  };
      +  services.mpd = {
      +    enable = true;
      +    musicDirectory = "/media";
      +    user = "mpd";
      +    group = "mpd";
      +    network = {
      +      port = 3254;
      +      listenAddress = "any";
      +    };
      +    credentials = [
      +      {
      +        passwordFile = config.sops.secrets.mpdpass.path;
      +        permissions = [
      +          "read"
      +          "add"
      +          "control"
      +          "admin"
      +        ];
      +      }
      +    ];
      +  };
      +}
      +
      +
      +
      +
    2. +
    +
  14. + +
  15. Spotifyd
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +    listenAddresses = [{
      +      port = 22;
      +      addr = "0.0.0.0";
      +    }];
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +
      +
      +  proxmoxLXC.privileged = true; # manage hostname myself
      +
      +  users.groups.spotifyd = {
      +    gid = 65136;
      +  };
      +
      +  users.users.spotifyd = {
      +    isSystemUser = true;
      +    uid = 65136;
      +    group = "spotifyd";
      +    extraGroups  = [ "audio" "utmp" ];
      +  };
      +
      +  sound = {
      +    enable = true;
      +  };
      +
      +  hardware.enableAllFirmware = true;
      +  networking.hostName = "spotifyd"; # Define your hostname.
      +  networking.firewall.enable = false;
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +  ];
      +
      +  # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  # sops.defaultSopsFile = "/.dotfiles/secrets/spotifyd/secrets.yaml";
      +  # sops.validateSopsFiles = false;
      +
      +  services.spotifyd = {
      +    enable = true;
      +    settings = {
      +      global = {
      +        dbus_type = "session";
      +        use_mpris = false;
      +        device = "default:CARD=PCH";
      +        device_name = "SwarselSpot";
      +        mixer = "alsa";
      +        zeroconf_port = 1025;
      +      };
      +    };
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  16. + +
  17. Sync
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +  imports = [
      +    ./hardware-configuration.nix
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +  ];
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/root/.dotfiles/secrets/sync/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.swarsel = { owner = "root";};
      +  sops.secrets.dnstokenfull = {owner="acme";};
      +  sops.templates."certs.secret".content = ''
      +  CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
      +  '';
      +
      +  security.acme = {
      +    acceptTerms = true;
      +    preliminarySelfsigned = false;
      +    defaults.email = "mrswarsel@gmail.com";
      +    defaults.dnsProvider = "cloudflare";
      +    defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
      +  };
      +
      +  services.nginx = {
      +    enable = true;
      +    recommendedProxySettings = true;
      +    recommendedTlsSettings = true;
      +    recommendedOptimisation = true;
      +    recommendedGzipSettings = true;
      +    virtualHosts = {
      +
      +      "synki.swarsel.win" = {
      +        enableACME = true;
      +        forceSSL = true;
      +        acmeRoot = null;
      +        locations = {
      +          "/" = {
      +            proxyPass = "http://localhost:27701";
      +            extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +          };
      +        };
      +      };
      +
      +        "sync.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://localhost:8384/";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +
      +        "swagit.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "http://localhost:3000";
      +              extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +            };
      +          };
      +        };
      +    };
      +  };
      +
      +  boot.tmp.cleanOnBoot = true;
      +  zramSwap.enable = false;
      +  networking.hostName = "sync";
      +  networking.enableIPv6 = false;
      +  networking.domain = "subnet03112148.vcn03112148.oraclevcn.com";
      +  networking.firewall.extraCommands = ''
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 27701 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 8384 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 3000 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 22000 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p udp --dport 22000 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p udp --dport 21027 -j ACCEPT
      +  '';
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +  boot.loader.grub.device = "nodev";
      +
      +  services.anki-sync-server = {
      +    enable = true;
      +    port = 27701;
      +    address = "0.0.0.0";
      +    openFirewall = true;
      +    users = [
      +      {
      +      username = "Swarsel";
      +      passwordFile = config.sops.secrets.swarsel.path;
      +      }
      +    ];
      +  };
      +
      +  services.syncthing = {
      +    enable = true;
      +    guiAddress = "0.0.0.0:8384";
      +    openDefaultPorts = true;
      +  };
      +
      +  services.forgejo = {
      +    enable = true;
      +    settings = {
      +      DEFAULT = {
      +        APP_NAME = "~SwaGit~";
      +      };
      +      server = {
      +        PROTOCOL = "http";
      +        HTTP_PORT = 3000;
      +        HTTP_ADDR = "0.0.0.0";
      +        DOMAIN = "swagit.swarsel.win";
      +        ROOT_URL = "https://swagit.swarsel.win";
      +      };
      +      service = {
      +        DISABLE_REGISTRATION = true;
      +        SHOW_REGISTRATION_BUTTON = false;
      +      };
      +    };
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  18. + +
  19. [Manual steps required] Swatrix
    +
    +
    +
      +
    1. NixOS
      +
      +

      +The files mentioned by +

      + +
      +
      +settings.app_service_config_files = [
      +    "/var/lib/matrix-synapse/telegram-registration.yaml"
      +    "/var/lib/matrix-synapse/whatsapp-registration.yaml"
      +    "/var/lib/matrix-synapse/signal-registration.yaml"
      +    "/var/lib/matrix-synapse/doublepuppet.yaml"
      +  ]
      +
      +
      +
      + +

      +need to be moved to the corresponding location. The below files are created as soon as the appservice is run once. This means that matrix will crash on the first startup; afterwards run these commands and restart the service. +

      + +
      +
      +cp /var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse/
      +chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/telegram-registration.yaml
      +cp /var/lib/mautrix-signal/signal-registration.yaml /var/lib/matrix-synapse/
      +chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/signal-registration.yaml
      +cp /var/lib/mautrix-whatsapp/whatsapp-registration.yaml /var/lib/matrix-synapse/
      +chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/whatsapp-registration.yaml
      +
      +
      +
      + +

      +as for the contents of doublepuppet.yaml: +

      + +
      +
      id: doublepuppet
      +url:
      +as_token: doublepuppet
      +hs_token: notused
      +sender_localpart: notused
      +rate_limited: false
      +namespaces:
      +  users:
      +  - regex: '@.*:matrix2\.swarsel\.win'
      +    exclusive: false
      +
      +
      + +

      +Lastly, the machine that runs matrix needs to regularly update, as otherwise you will lose connectivity to the bridges. +

      + +
      +
      +{ config, pkgs, modulesPath, unstable, sops, ... }: let
      +  matrixDomain = "swatrix.swarsel.win";
      +in {
      +
      +  imports = [
      +    ./hardware-configuration.nix
      +    # (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix") # no longer needed; mautrix-signal was added to nixpkgs
      +  ];
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +    matrix-synapse
      +    lottieconverter
      +    ffmpeg
      +  ];
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/root/.dotfiles/secrets/omatrix/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.dnstokenfull = {owner="acme";};
      +  sops.templates."certs.secret".content = ''
      +  CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
      +  '';
      +
      +  documentation = {
      +    enable = false;
      +  };
      +
      +  security.acme = {
      +    acceptTerms = true;
      +    preliminarySelfsigned = false;
      +    defaults.email = "mrswarsel@gmail.com";
      +    defaults.dnsProvider = "cloudflare";
      +    defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
      +  };
      +
      +  services.nginx = {
      +    enable = true;
      +    recommendedProxySettings = true;
      +    recommendedTlsSettings = true;
      +    recommendedOptimisation = true;
      +    recommendedGzipSettings = true;
      +    virtualHosts = {
      +
      +      "swatrix.swarsel.win" = {
      +        enableACME = true;
      +        forceSSL = true;
      +        acmeRoot = null;
      +        locations = {
      +          "~ ^(/_matrix|/_synapse/client)" = {
      +            proxyPass = "http://localhost:8008";
      +            extraConfig = ''
      +                client_max_body_size 0;
      +              '';
      +          };
      +        };
      +      };
      +    };
      +  };
      +
      +  boot.tmp.cleanOnBoot = true;
      +  zramSwap.enable = false;
      +  networking.hostName = "swatrix";
      +  networking.enableIPv6 = false;
      +  # networking.domain = "subnet03112148.vcn03112148.oraclevcn.com";
      +  networking.domain = "swarsel.win";
      +  networking.firewall.extraCommands = ''
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 8008 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 29317 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 29318 -j ACCEPT
      +  iptables -I INPUT -m state --state NEW -p tcp --dport 29328 -j ACCEPT
      +  '';
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +  boot.loader.grub.device = "nodev";
      +
      +  sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
      +  sops.templates."matrix_user_register.sh".content = ''
      +  register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
      +  '';
      +  sops.templates.matrixshared.owner = "matrix-synapse";
      +  sops.templates.matrixshared.content = ''
      +  registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
      +  '';
      +  sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
      +  sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
      +  sops.templates.mautrixtelegram.owner = "matrix-synapse";
      +  sops.templates.mautrixtelegram.content = ''
      +  MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
      +  MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
      +  MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
      +  MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
      +  '';
      +  # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
      +  # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
      +  # sops.templates.mautrixwhatsapp.content = ''
      +  # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
      +  # '';
      +
      +  services.postgresql.enable = true;
      +  services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
      +    CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
      +    CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
      +    CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
      +    CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +    CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
      +    CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
      +      TEMPLATE template0
      +      LC_COLLATE = "C"
      +      LC_CTYPE = "C";
      +  '';
      +
      +  services.matrix-synapse = {
      +    settings.app_service_config_files = [
      +      "/var/lib/matrix-synapse/telegram-registration.yaml"
      +      "/var/lib/matrix-synapse/whatsapp-registration.yaml"
      +      "/var/lib/matrix-synapse/signal-registration.yaml"
      +      "/var/lib/matrix-synapse/doublepuppet.yaml"
      +    ];
      +    enable = true;
      +    settings.server_name = matrixDomain;
      +    settings.public_baseurl = "https://${matrixDomain}";
      +    extraConfigFiles = [
      +      config.sops.templates.matrixshared.path
      +    ];
      +    settings.listeners = [
      +      { port = 8008;
      +        bind_addresses = [ "0.0.0.0" ];
      +        type = "http";
      +        tls = false;
      +        x_forwarded = true;
      +        resources = [
      +          {
      +            names = [ "client" "federation" ];
      +            compress = true;
      +          }
      +        ];
      +      }
      +    ];
      +  };
      +
      +  services.mautrix-telegram = {
      +    enable = true;
      +    environmentFile = config.sops.templates.mautrixtelegram.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +        address= "http://localhost:29317";
      +        hostname = "0.0.0.0";
      +        port = "29317";
      +        provisioning.enabled = true;
      +        id = "telegram";
      +        # ephemeral_events = true; # not needed due to double puppeting
      +        public = {
      +          enabled = false;
      +        };
      +        database = "postgresql:///mautrix-telegram?host=/run/postgresql";
      +      };
      +      bridge = {
      +        # login_shared_secret_map = {
      +          # matrixDomain = "as_token:doublepuppet";
      +        # };
      +        relaybot.authless_portals = true;
      +        allow_avatar_remove = true;
      +        allow_contact_info = true;
      +        sync_channel_members = true;
      +        startup_sync = true;
      +        sync_create_limit = 0;
      +        sync_direct_chats = true;
      +        telegram_link_preview = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +        # Animated stickers conversion requires additional packages in the
      +        # service's path.
      +        # If this isn't a fresh installation, clearing the bridge's uploaded
      +        # file cache might be necessary (make a database backup first!):
      +        # delete from telegram_file where \
      +        #   mime_type in ('application/gzip', 'application/octet-stream')
      +        animated_sticker = {
      +          target = "gif";
      +          args = {
      +            width = 256;
      +            height = 256;
      +            fps = 30;               # only for webm
      +            background = "020202";  # only for gif, transparency not supported
      +          };
      +        };
      +      };
      +    };
      +  };
      +  systemd.services.mautrix-telegram.path = with pkgs; [
      +    lottieconverter  # for animated stickers conversion, unfree package
      +    ffmpeg           # if converting animated stickers to webm (very slow!)
      +  ];
      +
      +  services.mautrix-whatsapp = {
      +    enable = true;
      +    # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +        address= "http://localhost:29318";
      +        hostname = "0.0.0.0";
      +        port = 29318;
      +        database = {
      +          type = "postgres";
      +          uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
      +        };
      +      };
      +      bridge = {
      +        displayname_template = "{{or .FullName .PushName .JID}} (WA)";
      +        history_sync = {
      +          backfill = true;
      +          max_initial_conversations = -1;
      +          message_count = -1;
      +          request_full_sync = true;
      +          full_sync_config = {
      +            days_limit = 900;
      +            size_mb_limit = 5000;
      +            storage_quota_mb = 5000;
      +          };
      +        };
      +        login_shared_secret_map = {
      +          matrixDomain = "as_token:doublepuppet";
      +        };
      +        sync_manual_marked_unread = true;
      +        send_presence_on_typing = true;
      +        parallel_member_sync = true;
      +        url_previews = true;
      +        caption_in_message = true;
      +        extev_polls = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +      };
      +    };
      +  };
      +
      +  services.mautrix-signal = {
      +    enable = true;
      +    registerToSynapse = false; # this has the same effect as registering to app_service_config_file above
      +    # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +    settings = {
      +      homeserver = {
      +        address = "http://localhost:8008";
      +        domain = matrixDomain;
      +      };
      +      appservice = {
      +
      +        address= "http://localhost:29328";
      +        hostname = "0.0.0.0";
      +        port = 29328;
      +        database = {
      +          type = "postgres";
      +          uri = "postgresql:///mautrix-signal?host=/run/postgresql";
      +        };
      +      };
      +      bridge = {
      +        displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
      +        login_shared_secret_map = {
      +          matrixDomain = "as_token:doublepuppet";
      +        };
      +        caption_in_message = true;
      +        permissions = {
      +          "*" = "relaybot";
      +          "@swarsel:${matrixDomain}" = "admin";
      +        };
      +      };
      +    };
      +  };
      +
      +  # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
      +  # messages out after a while.
      +
      +  systemd.timers."restart-bridges" = {
      +    wantedBy = [ "timers.target" ];
      +    timerConfig = {
      +      OnBootSec = "1d";
      +      OnUnitActiveSec = "1d";
      +      Unit = "restart-bridges.service";
      +    };
      +  };
      +
      +  systemd.services."restart-bridges" = {
      +    script = ''
      +    systemctl restart mautrix-whatsapp.service
      +    systemctl restart mautrix-signal.service
      +    systemctl restart mautrix-telegram.service
      +    '';
      +    serviceConfig = {
      +      Type = "oneshot";
      +      User = "root";
      +    };
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  20. + +
  21. Paperless
    +
    +
    +
      +
    1. NixOS
      +
      +
      +
      +{ config, pkgs, modulesPath, ... }:
      +
      +{
      +
      +  imports = [
      +    (modulesPath + "/virtualisation/proxmox-lxc.nix")
      +    ./hardware-configuration.nix
      +  ];
      +
      +
      +
      +  services.xserver = {
      +    layout = "us";
      +    xkbVariant = "altgr-intl";
      +  };
      +  nix.settings.experimental-features = ["nix-command" "flakes"];
      +  proxmoxLXC.manageNetwork = true; # manage network myself
      +  proxmoxLXC.manageHostName = false; # manage hostname myself
      +  networking.useDHCP = true;
      +  networking.enableIPv6 = false;
      +  services.openssh = {
      +    enable = true;
      +    settings.PermitRootLogin = "yes";
      +    listenAddresses = [{
      +      port = 22;
      +      addr = "0.0.0.0";
      +    }];
      +  };
      +  users.users.root.openssh.authorizedKeys.keyFiles = [
      +    ../../../secrets/keys/authorized_keys
      +  ];
      +
      +  system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +  environment.shellAliases = {
      +    nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +  };
      +
      +
      +
      +  users.groups.lxc_shares = {
      +    gid = 10000;
      +    members = [
      +      "paperless"
      +      "root"
      +    ];
      +  };
      +
      +  environment.systemPackages = with pkgs; [
      +    git
      +    gnupg
      +    ssh-to-age
      +  ];
      +
      +  networking.hostName = "paperless"; # Define your hostname.
      +  networking.firewall.enable = false;
      +
      +  sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +  sops.defaultSopsFile = "/root/.dotfiles/secrets/paperless/secrets.yaml";
      +  sops.validateSopsFiles = false;
      +  sops.secrets.admin = { owner = "paperless";};
      +
      +  services.paperless = {
      +    enable = true;
      +    mediaDir = "/media";
      +    user = "paperless";
      +    port = 28981;
      +    passwordFile = config.sops.secrets.admin.path;
      +    address = "0.0.0.0";
      +    extraConfig = {
      +      PAPERLESS_OCR_LANGUAGE = "deu+eng";
      +      PAPERLESS_URL = "scan.swarsel.win";
      +      PAPERLESS_OCR_USER_ARGS = builtins.toJSON {
      +        optimize = 1;
      +        pdfa_image_compression = "lossless";
      +      };
      +    };
      +  };
      +
      +}
      +
      +
      +
      +
      +
    2. +
    +
  22. + +
  23. Sandbox (Lenovo Y510P)
    +
    +

    +My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately. +

    +
    + +
      +
    1. NixOS
      +
      +
      +
      +    { config, pkgs, modulesPath, unstable, sops, ... }: let
      +    matrixDomain = "swatrix.swarsel.win";
      +  in {
      +
      +    imports = [
      +      ./hardware-configuration.nix
      +      # we import here a service that is not available yet on normal nixpkgs
      +      # this module is hence not in the modules list, we add it ourselves
      +      (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix")
      +    ];
      +
      +      boot.loader.grub = {
      +        enable = true;
      +        device = "/dev/sda";
      +        useOSProber = true;
      +      };
      +
      +      users.users.swarsel = {
      +        isNormalUser = true;
      +        description = "Leon S";
      +        extraGroups = [ "networkmanager" "wheel" "lp"];
      +        packages = with pkgs; [];
      +      };
      +
      +  # actual config starts here
      +
      +    fileSystems."/mnt/Eternor" = {
      +      device = "//192.168.1.3/Eternor";
      +      fsType = "cifs";
      +      options = let
      +        # this line prevents hanging on network split
      +        automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
      +      in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"];
      +    };
      +
      +      environment.systemPackages = with pkgs; [
      +      git
      +      gnupg
      +      ssh-to-age
      +      lego
      +      nginx
      +      calibre
      +      openvpn
      +      jq
      +      iptables
      +      busybox
      +      wireguard-tools
      +      matrix-synapse
      +      lottieconverter
      +      ffmpeg
      +      pciutils
      +      alsa-utils
      +      mpv
      +      zfs
      +      ];
      +
      +      services.xserver = {
      +        layout = "us";
      +        xkbVariant = "altgr-intl";
      +      };
      +
      +      nix.settings.experimental-features = ["nix-command" "flakes"];
      +
      +      services.openssh = {
      +        enable = true;
      +        settings.PermitRootLogin = "yes";
      +        listenAddresses = [{
      +          port = 22;
      +          addr = "0.0.0.0";
      +        }];
      +      };
      +      users.users.root.openssh.authorizedKeys.keyFiles = [
      +        ../../secrets/keys/authorized_keys
      +      ];
      +
      +      system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
      +
      +      environment.shellAliases = {
      +        nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
      +      };
      +
      +boot.supportedFilesystems = [ "zfs" ];
      +boot.zfs.forceImportRoot = false;
      +networking.hostId = "8a8ad84a";
      +
      +      networking.hostName = "sandbox"; # Define your hostname.
      +      networking.enableIPv6 = true;
      +      networking.firewall.enable = false;
      +
      +      documentation = {
      +        enable = false;
      +      };
      +
      +    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      +    sops.defaultSopsFile = "/root/.dotfiles/secrets/sandbox/secrets.yaml";
      +    sops.validateSopsFiles = false;
      +    sops.secrets.dnstokenfull = {owner="acme";};
      +    sops.templates."certs.secret".content = ''
      +    CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
      +    '';
      +
      +    security.acme = {
      +      acceptTerms = true;
      +      preliminarySelfsigned = false;
      +      defaults.email = "mrswarsel@gmail.com";
      +      defaults.dnsProvider = "cloudflare";
      +      defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
      +    };
      +
      +    services.nginx = {
      +      enable = true;
      +      recommendedProxySettings = true;
      +      recommendedTlsSettings = true;
      +      recommendedOptimisation = true;
      +      recommendedGzipSettings = true;
      +      virtualHosts = {
      +
      +        "stash.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "/" = {
      +              proxyPass = "https://192.168.1.5";
      +              extraConfig = ''
      +              client_max_body_size 0;
      +              '';
      +            };
      +            # "/push/" = {
      +              # proxyPass = "http://192.168.2.5:7867";
      +            # };
      +            "/.well-known/carddav" = {
      +              return = "301 $scheme://$host/remote.php/dav";
      +            };
      +            "/.well-known/caldav" = {
      +              return = "301 $scheme://$host/remote.php/dav";
      +            };
      +          };
      +        };
      +
      +        "swatrix.swarsel.win" = {
      +          enableACME = true;
      +          forceSSL = true;
      +          acmeRoot = null;
      +          locations = {
      +            "~ ^(/_matrix|/_synapse/client)" = {
      +              proxyPass = "http://127.0.0.1:8008";
      +              extraConfig = ''
      +                  client_max_body_size 0;
      +                '';
      +            };
      +          };
      +        };
      +
      +
      +          "sound.swarsel.win" = {
      +            enableACME = true;
      +            forceSSL = true;
      +            acmeRoot = null;
      +            locations = {
      +              "/" = {
      +                proxyPass = "http://127.0.0.1:4040";
      +                proxyWebsockets = true;
      +                extraConfig = ''
      +                  proxy_redirect          http:// https://;
      +                  proxy_read_timeout      600s;
      +                  proxy_send_timeout      600s;
      +                  proxy_buffering         off;
      +                  proxy_request_buffering off;
      +                  client_max_body_size    0;
      +                '';
      +              };
      +            };
      +          };
      +
      +          "scan.swarsel.win" = {
      +            enableACME = true;
      +            forceSSL = true;
      +            acmeRoot = null;
      +            locations = {
      +              "/" = {
      +                proxyPass = "http://127.0.0.1:28981";
      +                extraConfig = ''
      +                  client_max_body_size 0;
      +                '';
      +              };
      +            };
      +          };
      +
      +          "screen.swarsel.win" = {
      +            enableACME = true;
      +            forceSSL = true;
      +            acmeRoot = null;
      +            locations = {
      +              "/" = {
      +                proxyPass = "http://127.0.0.1:8096";
      +                extraConfig = ''
      +                  client_max_body_size 0;
      +                '';
      +              };
      +            };
      +          };
      +
      +          "scroll.swarsel.win" = {
      +            enableACME = true;
      +            forceSSL = true;
      +            acmeRoot = null;
      +            locations = {
      +              "/" = {
      +                proxyPass = "http://127.0.0.1:8080";
      +                extraConfig = ''
      +                  client_max_body_size 0;
      +                '';
      +              };
      +            };
      +          };
      +
      +
      +        };
      +      };
      +
      +
      +    sops.secrets.kavita = { owner = "kavita";};
      +
      +    services.kavita = {
      +      enable = true;
      +      user = "kavita";
      +      port = 8080;
      +      tokenKeyFile = config.sops.secrets.kavita.path;
      +    };
      +
      +    users.users.jellyfin = {
      +      extraGroups  = [ "video" "render" ];
      +    };
      +
      +     # nixpkgs.config.packageOverrides = pkgs: {
      +     #   vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
      +     # };
      +
      +     hardware.opengl = {
      +       enable = true;
      +       extraPackages = with pkgs; [
      +         intel-media-driver # LIBVA_DRIVER_NAME=iHD
      +         vaapiIntel         # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
      +         vaapiVdpau
      +         libvdpau-va-gl
      +       ];
      +     };
      +
      +    services.jellyfin = {
      +      enable = true;
      +      user = "jellyfin";
      +      # openFirewall = true; # this works only for the default ports
      +    };
      +
      +                users.groups.vpn = {};
      +
      +                users.users.vpn = {
      +                  isNormalUser = true;
      +                  group = "vpn";
      +                  home = "/home/vpn";
      +                };
      +
      +                boot.kernelModules = [ "tun" ];
      +
      +                services.radarr = {
      +                  enable = true;
      +                };
      +
      +                services.readarr = {
      +                  enable = true;
      +                };
      +                services.sonarr = {
      +                  enable = true;
      +                };
      +                services.lidarr = {
      +                  enable = true;
      +                };
      +                services.prowlarr = {
      +                  enable = true;
      +                };
      +
      +                networking.firewall.extraCommands = ''
      +                sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP
      +                '';
      +                networking.iproute2 = {
      +                  enable = true;
      +                  rttablesExtraConfig = ''
      +                  200     vpn
      +                  '';
      +                };
      +                boot.kernel.sysctl = {
      +                  "net.ipv4.conf.all.rp_filter" = 2;
      +                  "net.ipv4.conf.default.rp_filter" = 2;
      +                  "net.ipv4.conf.enp7s0.rp_filter" = 2;
      +                };
      +                environment.etc = {
      +                  "openvpn/iptables.sh" =
      +                    { source = ../../scripts/server1/iptables.sh;
      +                      mode = "0755";
      +                    };
      +                  "openvpn/update-resolv-conf" =
      +                    { source = ../../scripts/server1/update-resolv-conf;
      +                      mode = "0755";
      +                    };
      +                  "openvpn/routing.sh" =
      +                    { source = ../../scripts/server1/routing.sh;
      +                      mode = "0755";
      +                    };
      +                  "openvpn/ca.rsa.2048.crt" =
      +                    { source = ../../secrets/certs/ca.rsa.2048.crt;
      +                      mode = "0644";
      +                    };
      +                  "openvpn/crl.rsa.2048.pem" =
      +                    { source = ../../secrets/certs/crl.rsa.2048.pem;
      +                      mode = "0644";
      +                    };
      +                };
      +
      +                sops.secrets.vpnuser = {};
      +                sops.secrets.rpcuser = {owner="vpn";};
      +                sops.secrets.vpnpass = {};
      +                sops.secrets.rpcpass = {owner="vpn";};
      +                sops.secrets.vpnprot = {};
      +                sops.secrets.vpnloc = {};
      +                # sops.secrets.crlpem = {};
      +                # sops.secrets.capem = {};
      +                sops.templates."transmission-rpc".owner = "vpn";
      +                sops.templates."transmission-rpc".content = builtins.toJSON {
      +                  rpc-username = config.sops.placeholder.rpcuser;
      +                  rpc-password = config.sops.placeholder.rpcpass;
      +                };
      +
      +                sops.templates.pia.content = ''
      +                ${config.sops.placeholder.vpnuser}
      +                ${config.sops.placeholder.vpnpass}
      +                '';
      +
      +                sops.templates.vpn.content = ''
      +                  client
      +                  dev tun
      +                  proto ${config.sops.placeholder.vpnprot}
      +                  remote ${config.sops.placeholder.vpnloc}
      +                  resolv-retry infinite
      +                  nobind
      +                  persist-key
      +                  persist-tun
      +                  cipher aes-128-cbc
      +                  auth sha1
      +                  tls-client
      +                  remote-cert-tls server
      +
      +                  auth-user-pass ${config.sops.templates.pia.path}
      +                  compress
      +                  verb 1
      +                  reneg-sec 0
      +
      +                  crl-verify /etc/openvpn/crl.rsa.2048.pem
      +                  ca /etc/openvpn/ca.rsa.2048.crt
      +
      +                  disable-occ
      +                '';
      +
      +            services.openvpn.servers = {
      +              pia = {
      +                autoStart = true;
      +                updateResolvConf = false;
      +                config = "config ${config.sops.templates.vpn.path}";
      +              };
      +            };
      +
      +          services.transmission = {
      +            enable = true;
      +            credentialsFile = config.sops.templates."transmission-rpc".path;
      +            user = "vpn";
      +            settings = {
      +
      +            alt-speed-down= 8000;
      +            alt-speed-enabled= false;
      +            alt-speed-time-begin= 0;
      +            alt-speed-time-day= 127;
      +            alt-speed-time-enabled= true;
      +            alt-speed-time-end= 360;
      +            alt-speed-up= 2000;
      +            bind-address-ipv4= "0.0.0.0";
      +            bind-address-ipv6= "::";
      +            blocklist-enabled= false;
      +            blocklist-url= "http://www.example.com/blocklist";
      +            cache-size-mb= 256;
      +            dht-enabled= false;
      +            download-dir= "/test";
      +            download-limit= 100;
      +            download-limit-enabled= 0;
      +            download-queue-enabled= true;
      +            download-queue-size= 5;
      +            encryption= 2;
      +            idle-seeding-limit= 30;
      +            idle-seeding-limit-enabled= false;
      +            incomplete-dir= "/var/lib/transmission-daemon/Downloads";
      +            incomplete-dir-enabled= false;
      +            lpd-enabled= false;
      +            max-peers-global= 200;
      +            message-level= 1;
      +            peer-congestion-algorithm= "";
      +            peer-id-ttl-hours= 6;
      +            peer-limit-global= 100;
      +            peer-limit-per-torrent= 40;
      +            peer-port= 22371;
      +            peer-port-random-high= 65535;
      +            peer-port-random-low= 49152;
      +            peer-port-random-on-start= false;
      +            peer-socket-tos= "default";
      +            pex-enabled= false;
      +            port-forwarding-enabled= false;
      +            preallocation= 1;
      +            prefetch-enabled= true;
      +            queue-stalled-enabled= true;
      +            queue-stalled-minutes= 30;
      +            ratio-limit= 2;
      +            ratio-limit-enabled= false;
      +            rename-partial-files= true;
      +            rpc-authentication-required= true;
      +            rpc-bind-address= "0.0.0.0";
      +            rpc-enabled= true;
      +            rpc-host-whitelist= "";
      +            rpc-host-whitelist-enabled= true;
      +            rpc-port= 9091;
      +            rpc-url= "/transmission/";
      +            rpc-whitelist= "127.0.0.1,192.168.3.2";
      +            rpc-whitelist-enabled= true;
      +            scrape-paused-torrents-enabled= true;
      +            script-torrent-done-enabled= false;
      +            seed-queue-enabled= false;
      +            seed-queue-size= 10;
      +            speed-limit-down= 6000;
      +            speed-limit-down-enabled= true;
      +            speed-limit-up= 500;
      +            speed-limit-up-enabled= true;
      +            start-added-torrents= true;
      +            trash-original-torrent-files= false;
      +            umask= 2;
      +            upload-limit= 100;
      +            upload-limit-enabled= 0;
      +            upload-slots-per-torrent= 14;
      +            utp-enabled= false;
      +            };
      +          };
      +
      +        # services.nginx = {
      +        #       enable = true;
      +        #       virtualHosts = {
      +
      +        #         "192.168.1.192" = {
      +        #           locations = {
      +        #             "/transmission" = {
      +        #               proxyPass = "http://127.0.0.1:9091";
      +        #               extraConfig = ''
      +        #               proxy_set_header Host $host;
      +        #               proxy_set_header X-Real-IP $remote_addr;
      +        #               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      +        #               '';
      +        #             };
      +        #           };
      +        #         };
      +        #       };
      +        # };
      +
      +
      +    # sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
      +    # sops.templates."matrix_user_register.sh".content = ''
      +    # register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
      +    # '';
      +    # sops.templates.matrixshared.owner = "matrix-synapse";
      +    # sops.templates.matrixshared.content = ''
      +    # registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
      +    # '';
      +    # sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
      +    # sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
      +    # sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
      +    # sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
      +    # sops.templates.mautrixtelegram.owner = "matrix-synapse";
      +    # sops.templates.mautrixtelegram.content = ''
      +    # MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
      +    # MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
      +    # MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
      +    # MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
      +    # '';
      +
      +
      +
      +
      +    # ----------------
      +    # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
      +    # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
      +    # sops.templates.mautrixwhatsapp.content = ''
      +    # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
      +    # '';
      +
      +    services.postgresql.enable = true;
      +    services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
      +      CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
      +      CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
      +        TEMPLATE template0
      +        LC_COLLATE = "C"
      +        LC_CTYPE = "C";
      +      CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
      +      CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
      +        TEMPLATE template0
      +        LC_COLLATE = "C"
      +        LC_CTYPE = "C";
      +      CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
      +      CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
      +        TEMPLATE template0
      +        LC_COLLATE = "C"
      +        LC_CTYPE = "C";
      +      CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
      +      CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
      +        TEMPLATE template0
      +        LC_COLLATE = "C"
      +        LC_CTYPE = "C";
      +    '';
      +
      +    services.matrix-synapse = {
      +      settings.app_service_config_files = [
      +        "/var/lib/matrix-synapse/telegram-registration.yaml"
      +        "/var/lib/matrix-synapse/whatsapp-registration.yaml"
      +        "/var/lib/matrix-synapse/signal-registration.yaml"
      +        "/var/lib/matrix-synapse/doublepuppet.yaml"
      +      ];
      +      enable = false;
      +      settings.server_name = matrixDomain;
      +      settings.public_baseurl = "https://${matrixDomain}";
      +      extraConfigFiles = [
      +        config.sops.templates.matrixshared.path
      +      ];
      +      settings.listeners = [
      +        { port = 8008;
      +          bind_addresses = [ "0.0.0.0" ];
      +          type = "http";
      +          tls = false;
      +          x_forwarded = true;
      +          resources = [
      +            {
      +              names = [ "client" "federation" ];
      +              compress = true;
      +            }
      +          ];
      +        }
      +      ];
      +    };
      +
      +    services.mautrix-telegram = {
      +      enable = false;
      +      environmentFile = config.sops.templates.mautrixtelegram.path;
      +      settings = {
      +        homeserver = {
      +          address = "http://localhost:8008";
      +          domain = matrixDomain;
      +        };
      +        appservice = {
      +          address= "http://localhost:29317";
      +          hostname = "0.0.0.0";
      +          port = "29317";
      +          provisioning.enabled = true;
      +          id = "telegram";
      +          # ephemeral_events = true; # not needed due to double puppeting
      +          public = {
      +            enabled = false;
      +          };
      +          database = "postgresql:///mautrix-telegram?host=/run/postgresql";
      +        };
      +        bridge = {
      +          # login_shared_secret_map = {
      +            # matrixDomain = "as_token:doublepuppet";
      +          # };
      +          relaybot.authless_portals = true;
      +          allow_avatar_remove = true;
      +          allow_contact_info = true;
      +          sync_channel_members = true;
      +          startup_sync = true;
      +          sync_create_limit = 0;
      +          sync_direct_chats = true;
      +          telegram_link_preview = true;
      +          permissions = {
      +            "*" = "relaybot";
      +            "@swarsel:${matrixDomain}" = "admin";
      +          };
      +          # Animated stickers conversion requires additional packages in the
      +          # service's path.
      +          # If this isn't a fresh installation, clearing the bridge's uploaded
      +          # file cache might be necessary (make a database backup first!):
      +          # delete from telegram_file where \
      +          #   mime_type in ('application/gzip', 'application/octet-stream')
      +          animated_sticker = {
      +            target = "gif";
      +            args = {
      +              width = 256;
      +              height = 256;
      +              fps = 30;               # only for webm
      +              background = "020202";  # only for gif, transparency not supported
      +            };
      +          };
      +        };
      +      };
      +    };
      +    # systemd.services.mautrix-telegram.path = with pkgs; [
      +      # lottieconverter  # for animated stickers conversion, unfree package
      +      # ffmpeg           # if converting animated stickers to webm (very slow!)
      +    # ];
      +
      +    services.mautrix-whatsapp = {
      +      enable = false;
      +      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +      settings = {
      +        homeserver = {
      +          address = "http://localhost:8008";
      +          domain = matrixDomain;
      +        };
      +        appservice = {
      +          address= "http://localhost:29318";
      +          hostname = "0.0.0.0";
      +          port = 29318;
      +          database = {
      +            type = "postgres";
      +            uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
      +          };
      +        };
      +        bridge = {
      +          displayname_template = "{{or .FullName .PushName .JID}} (WA)";
      +          history_sync = {
      +            backfill = true;
      +            max_initial_conversations = -1;
      +            message_count = -1;
      +            request_full_sync = true;
      +            full_sync_config = {
      +              days_limit = 900;
      +              size_mb_limit = 5000;
      +              storage_quota_mb = 5000;
      +            };
      +          };
      +          login_shared_secret_map = {
      +            matrixDomain = "as_token:doublepuppet";
      +          };
      +          sync_manual_marked_unread = true;
      +          send_presence_on_typing = true;
      +          parallel_member_sync = true;
      +          url_previews = true;
      +          caption_in_message = true;
      +          extev_polls = true;
      +          permissions = {
      +            "*" = "relaybot";
      +            "@swarsel:${matrixDomain}" = "admin";
      +          };
      +        };
      +      };
      +    };
      +
      +    services.mautrix-signal = {
      +      enable = false;
      +      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      +      settings = {
      +        homeserver = {
      +          address = "http://localhost:8008";
      +          domain = matrixDomain;
      +        };
      +        appservice = {
      +
      +          address= "http://localhost:29328";
      +          hostname = "0.0.0.0";
      +          port = 29328;
      +          database = {
      +            type = "postgres";
      +            uri = "postgresql:///mautrix-signal?host=/run/postgresql";
      +          };
      +        };
      +        bridge = {
      +          displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
      +          login_shared_secret_map = {
      +            matrixDomain = "as_token:doublepuppet";
      +          };
      +          caption_in_message = true;
      +          permissions = {
      +            "*" = "relaybot";
      +            "@swarsel:${matrixDomain}" = "admin";
      +          };
      +        };
      +      };
      +    };
      +
      +    # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
      +    # messages out after a while.
      +
      +    systemd.timers."restart-bridges" = {
      +      wantedBy = [ "timers.target" ];
      +      timerConfig = {
      +        OnBootSec = "1d";
      +        OnUnitActiveSec = "1d";
      +        Unit = "restart-bridges.service";
      +      };
      +    };
      +
      +    systemd.services."restart-bridges" = {
      +      script = ''
      +      systemctl restart mautrix-whatsapp.service
      +      systemctl restart mautrix-signal.service
      +      systemctl restart mautrix-telegram.service
      +      '';
      +      serviceConfig = {
      +        Type = "oneshot";
      +        User = "root";
      +      };
      +    };
      +
      +
      +    users.groups.navidrome = {
      +      gid = 61593;
      +    };
      +
      +    users.groups.mpd = {};
      +
      +    users.users.navidrome = {
      +      isSystemUser = true;
      +      uid = 61593;
      +      group = "navidrome";
      +      extraGroups  = [ "audio" "utmp" ];
      +    };
      +
      +    users.users.mpd = {
      +      isSystemUser = true;
      +      group = "mpd";
      +      extraGroups  = [ "audio" "utmp" ];
      +    };
      +
      +    sound = {
      +      enable = true;
      +    };
      +
      +    hardware.enableAllFirmware = true;
      +
      +    sops.secrets.mpdpass = { owner = "mpd";};
      +
      +    services.navidrome = {
      +      enable = true;
      +      settings = {
      +        Address = "0.0.0.0";
      +        Port = 4040;
      +        MusicFolder = "/mnt/";
      +        EnableSharing = true;
      +        EnableTranscodingConfig = true;
      +        Scanner.GroupAlbumReleases = true;
      +        ScanSchedule = "@every 24h";
      +        # Insert these values locally as sops-nix does not work for them
      +        # LastFM.ApiKey = TEMPLATE;
      +        # LastFM.Secret = TEMPLATE;
      +        # Spotify.ID = TEMPLATE;
      +        # Spotify.Secret = TEMPLATE;
      +        UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png";
      +        UIWelcomeMessage = "~SwarselSound~";
      +      };
      +    };
      +    services.mpd = {
      +      enable = true;
      +      musicDirectory = "/mnt/Eternor/Musik";
      +      user = "mpd";
      +      group = "mpd";
      +      network = {
      +        port = 3254;
      +        listenAddress = "any";
      +      };
      +      credentials = [
      +        {
      +          passwordFile = config.sops.secrets.mpdpass.path;
      +          permissions = [
      +            "read"
      +            "add"
      +            "control"
      +            "admin"
      +          ];
      +        }
      +      ];
      +    };
      +
      +
      +    users.groups.spotifyd = {
      +      gid = 65136;
      +    };
      +
      +    users.users.spotifyd = {
      +      isSystemUser = true;
      +      uid = 65136;
      +      group = "spotifyd";
      +      extraGroups  = [ "audio" "utmp" ];
      +    };
      +
      +    services.spotifyd = {
      +      enable = true;
      +      settings = {
      +        global = {
      +          dbus_type = "session";
      +          use_mpris = false;
      +          device = "default:CARD=PCH";
      +          device_name = "SwarselSpot";
      +          mixer = "alsa";
      +          zeroconf_port = 1025;
      +        };
      +      };
      +    };
      +
      +      # Network shares
      +      # add a user with sudo smbpasswd -a <user>
      +      services.samba = {
      +        package = pkgs.samba4Full;
      +        extraConfig = ''
      +        workgroup = WORKGROUP
      +        server role = standalone server
      +        dns proxy = no
      +
      +        pam password change = yes
      +        map to guest = bad user
      +        create mask = 0664
      +        force create mode = 0664
      +        directory mask = 0775
      +        force directory mode = 0775
      +        follow symlinks = yes
      +        '';
      +
      +        # ^^ `samba4Full` is compiled with avahi, ldap, AD etc support compared to the default package, `samba`
      +        # Required for samba to register mDNS records for auto discovery
      +        # See https://github.com/NixOS/nixpkgs/blob/592047fc9e4f7b74a4dc85d1b9f5243dfe4899e3/pkgs/top-level/all-packages.nix#L27268
      +        enable = true;
      +        # openFirewall = true;
      +        shares.test = {
      +          browseable = "yes";
      +          "read only" = "no";
      +          "guest ok" = "no";
      +          path = "/test2";
      +          writable = "true";
      +          comment = "Eternor";
      +          "valid users" = "@smbtest2";
      +        };
      +      };
      +
      +
      +      services.avahi = {
      +        publish.enable = true;
      +        publish.userServices = true;
      +        # ^^ Needed to allow samba to automatically register mDNS records without the need for an `extraServiceFile`
      +        nssmdns = true;
      +        # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
      +  enable = true;
      +      };
      +
      +      services.samba-wsdd = {
      +      # This enables autodiscovery on windows since SMB1 (and thus netbios) support was discontinued
      +        enable = true;
      +      };
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +    }
      +
      +
      +
      +
      +
    2. +
    +
  24. +
+
+
+
+

3.2. Common NixOS

+
+

+These are system-level settings specific to NixOS machines. All settings that are required on all machines go here. +

+
+ +
+

3.2.1. General

+
+
+ +
    +
  1. Enable home-manager module
    +
    +

    +First, we enable the use of home-manager as a NixoS module +

    + +
    +
    +{ config, lib, pkgs, inputs, ... }:
    +
    +{
    +
    +  home-manager.useGlobalPkgs = true;
    +  home-manager.useUserPackages = true;
    +
    +
    +
    +
  2. + +
  3. Setup login keymap
    +
    +

    +Next, we setup the keymap in case we are not in a graphical session. At this point, I always resort to us/altgr-intl, as it is extremly comfortable to use +

    + +
    +
    services.xserver = {
    +  xkb.layout = "us";
    +  xkb.variant = "altgr-intl";
    +};
    +
    +
    +
    +
    +
  4. + +
  5. Enable flakes and nix-command
    +
    +

    +Next, we need to make sure that flakes stay enabled when we rebuild the configuration. At the same time we enable the experimental nix-command, which enables commands such as the more powerful nix shell as opposed to nix-shell. +

    + +
    +
    +nix.settings.experimental-features = ["nix-command" "flakes"];
    +
    +
    +
    +
    +
  6. + +
  7. Environment setup
    +
    +

    +Next, we will setup some environment variables that need to be set on the system-side. We apply some compatibility options for chromium apps on wayland, enable the wordlist and make metadata reading possible for my file explorer (nautilus). +

    + +
    +
    +# use ozone for wayland - chromium apps
    +  environment.sessionVariables.NIXOS_OZONE_WL = "1";
    +
    +  # wordlist for look
    +  environment.wordlist.enable = true;
    +  # gstreamer plugins for nautilus (used for file metadata)
    +  environment.sessionVariables.GST_PLUGIN_SYSTEM_PATH_1_0 = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (with pkgs.gst_all_1; [
    +    gst-plugins-good
    +    gst-plugins-bad
    +    gst-plugins-ugly
    +    gst-libav
    +  ]);
    +
    +
    +
    +
    +
  8. + +
  9. Make sure time is consistent in windows dualboot
    +
    +

    +Windows/Linux dualboot has the quirk of ruining the system clock. Fix it on this side. +

    + +
    +
    +time.hardwareClockInLocalTime = true;
    +
    +
    +
    +
    +
  10. + +
  11. Disallow stylix from styling grub
    +
    +

    +By default, stylix wants to style GRUB as well. However, I think that looks horrible. +

    + +
    +
    +# dont style GRUB with stylix
    +stylix.targets.grub.enable = false; # the styling makes grub more ugly
    +
    +
    +
    +
    +
  12. + +
  13. Enable PolicyKit
    +
    +

    +Needed for control over system-wide privileges etc. +

    + +
    +
    +security.polkit.enable = true;
    +
    +
    +
    +
    +
  14. + + +
  15. Reduce systemd timeouts
    +
    +

    +There is a persistent bug over Linux kernels that makes the user wait 1m30s on system shutdown due to the reason a stop job is running for session 1 of user .... I do not want to wait that long and am confident no important data is lost by doing this. +

    + +
    +
    +
    +  # systemd
    +  systemd.extraConfig = ''
    +  DefaultTimeoutStartSec=60s
    +  DefaultTimeoutStopSec=15s
    +'';
    +
    +
    +
    +
    +
  16. + +
  17. Hardware settings
    +
    +

    +Enable OpenGL, Sound, Bluetooth and various drivers. +

    + +
    +
    +hardware.opengl = {
    +  enable = true;
    +  driSupport = true;
    +  driSupport32Bit = true;
    +};
    +
    +sound.enable = true;
    +hardware.pulseaudio= {
    +  enable = true;
    +  package = pkgs.pulseaudioFull;
    +};
    +
    +hardware.enableAllFirmware = true;
    +
    +hardware.bluetooth.powerOnBoot = true;
    +hardware.bluetooth.settings = {
    +  General = {
    +    Enable = "Source,Sink,Media,Socket";
    +  };
    +};
    +
    +
    +
    +
    +
  18. + +
  19. Common network settings
    +
    +

    +Here I only enable networkmanager. Most of the 'real' network config is done in System specific configuration. +

    + +
    +
    +networking.networkmanager.enable = true;
    +
    +
    +
    +
    +
  20. + +
  21. Locale settings
    +
    +

    +Setup timezone and locale. I want to use the US layout, but have the rest adapted to my country and timezone. +

    + +
    +
    +time.timeZone = "Europe/Vienna";
    +
    +i18n.defaultLocale = "en_US.UTF-8";
    +i18n.extraLocaleSettings = {
    +  LC_ADDRESS = "de_AT.UTF-8";
    +  LC_IDENTIFICATION = "de_AT.UTF-8";
    +  LC_MEASUREMENT = "de_AT.UTF-8";
    +  LC_MONETARY = "de_AT.UTF-8";
    +  LC_NAME = "de_AT.UTF-8";
    +  LC_NUMERIC = "de_AT.UTF-8";
    +  LC_PAPER = "de_AT.UTF-8";
    +  LC_TELEPHONE = "de_AT.UTF-8";
    +  LC_TIME = "de_AT.UTF-8";
    +};
    +
    +
    +
    +
    +
  22. +
+
+ +
+

3.2.2. System Packages

+
+

+Mostly used to install some compilers and lsp's that I want to have available when not using a devShell flake. Most other packages should go in Installed packages. +

+ +
+
+environment.systemPackages = with pkgs; [
+  # yubikey packages
+  gnupg
+  yubikey-personalization
+  yubikey-personalization-gui
+  yubico-pam
+  # yubioath-flutter
+  # yubikey-manager
+  # yubikey-manager-qt
+  yubico-piv-tool
+  # pinentry
+
+  # theme related
+  gnome.adwaita-icon-theme
+
+  # kde-connect
+  xdg-desktop-portal
+
+  # bluetooth
+  bluez
+
+  # lsp-related -------------------------------
+  # nix
+  # latex
+  texlab
+  ghostscript_headless
+  # wireguard
+  wireguard-tools
+  # rust
+  rust-analyzer
+  clippy
+  rustfmt
+  # cpp
+  clang-tools
+  # + cuda
+  cudatoolkit
+  #lsp-bridge / python
+  gcc
+  gdb
+  (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server]))
+  # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server]))
+  # --------------------------------------------
+
+  (stdenv.mkDerivation {
+    name = "oama";
+
+    src = pkgs.fetchurl {
+      name = "oama";
+      url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz";
+      sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc=";
+    };
+
+    phases = [
+      "unpackPhase"
+    ];
+
+    unpackPhase = ''
+    mkdir -p $out/bin
+    tar xvf $src -C $out/
+    mv $out/oama-0.13.1-Linux-x86_64-static/oama $out/bin/
+    '';
+
+  })
+
+];
+
+
+
+
+
+ +
+

3.2.3. Programs (including zsh setup)

+
+

+Some programs profit from being installed through dedicated NixOS settings on system-level; these go here. Notably the zsh setup goes here and cannot be deleted under any circumstances. +

+ +
+
+programs.dconf.enable = true;
+programs.evince.enable = true;
+programs.kdeconnect.enable = true;
+
+
+# zsh section, do not delete ------
+programs.zsh.enable = true;
+users.defaultUserShell = pkgs.zsh;
+environment.shells = with pkgs; [ zsh ];
+environment.pathsToLink = [ "/share/zsh" ];
+# ---------------------------------
+
+
+
+
+
+ +
+

3.2.4. Services

+
+

+Setting up some hardware services as well as keyboard related settings. Here we make sure that we can use the CAPS key as a ESC/CTRL double key, which is a lifesaver. +

+
+ +
    +
  1. blueman
    +
    +

    +Enables the blueman service including the nice system tray icon. +

    + +
    +
    +services.blueman.enable = true;
    +
    +
    +
    +
    +
  2. + +
  3. Detect Scanners over network
    +
    +

    +This allows me to use my big scanner/printer's scanning function over the network. +

    + +
    +
    +# enable scanners over network
    +hardware.sane = {
    +  enable = true;
    +  extraBackends = [ pkgs.sane-airscan ];
    +};
    +
    +
    +
    +
    +
  4. + +
  5. Detect Printers over network
    +
    +

    +This allows me to use my big scanner/printer's printing function over the network. Avahi is the service used for the network discovery, the rest of the settings are driver related. +

    + + +
    +
    +  # enable discovery and usage of network devices (esp. printers)
    +  services.printing.enable = true;
    +  services.printing.drivers = [
    +    pkgs.gutenprint
    +    pkgs.gutenprintBin
    +  ];
    +  services.printing.browsedConf = ''
    +BrowseDNSSDSubTypes _cups,_print
    +BrowseLocalProtocols all
    +BrowseRemoteProtocols all
    +CreateIPPPrinterQueues All
    +
    +BrowseProtocols all
    +    '';
    +  services.avahi = {
    +    enable = true;
    +    nssmdns4 = true;
    +    openFirewall = true;
    +  };
    +
    +
    +
    +
    +
  6. + +
  7. enable GVfs
    +
    +

    +This is being set to allow myself to use all functions of nautilus in NixOS +

    + +
    +
    +services.gvfs.enable = true;
    +
    +
    +
    +
    +
  8. + +
  9. interception-tools: Make CAPS work as ESC/CTRL
    +
    +

    +This is a super-convenient package that lets my remap my CAPS key to ESC if pressed shortly, and CTRL if being held. +

    + +
    +
    +# Make CAPS work as a dual function ESC/CTRL key
    +services.interception-tools = {
    +  enable = true;
    +  udevmonConfig = let
    +    dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" ''
    +      TIMING:
    +        TAP_MILLISEC: 200
    +        DOUBLE_TAP_MILLISEC: 0
    +
    +      MAPPINGS:
    +        - KEY: KEY_CAPSLOCK
    +          TAP: KEY_ESC
    +          HOLD: KEY_LEFTCTRL
    +    '';
    +  in ''
    +    - JOB: |
    +        ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \
    +          | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \
    +          | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE
    +      DEVICE:
    +        EVENTS:
    +          EV_KEY: [KEY_CAPSLOCK]
    +  '';
    +};
    +
    +
    +
    +
    +
  10. +
+
+ +
+

3.2.5. Yubikey settings

+
+

+It makes sense to house these settings in their own section, since they are all needed really. Note that the starting of the gpg-agent is done in the sway settings, to also perform this step of the setup for non NixOS-machines at the same time. +

+ +

+pcscd is needed to use the smartcard mode (CCID) of the Yubikey. +

+ +

+The exception is the system packages, since that cannot be defined twice in the same file (common.nix). The comment is left in as a remider for that. +

+ +

+The rest of the gpg-agent related settings are here: gpg-agent +

+ +
+
+programs.ssh.startAgent = false;
+
+services.pcscd.enable = true;
+
+# environment.systemPackages = with pkgs; [
+# --- IN SYSTEM PACKAGES SECTION ---
+# ];
+
+services.udev.packages = with pkgs; [
+  yubikey-personalization
+];
+
+
+
+
+
+
+ +
+

3.2.6. System Login

+
+

+This section houses the greetd related settings. I do not really want to use a display manager, but it is useful to have setup in some ways - in my case for starting sway on system startup. Notably the default user login setting that is commented out here goes into the system specific settings, make sure to update it there +

+ +
+
+services.greetd = {
+  enable = true;
+  settings = {
+    initial_session.command = "sway";
+    # initial_session.user ="swarsel";
+    default_session.command = ''
+                ${pkgs.greetd.tuigreet}/bin/tuigreet \
+                  --time \
+                  --asterisks \
+                  --user-menu \
+                  --cmd sway
+              '';
+  };
+};
+
+environment.etc."greetd/environments".text = ''
+              sway
+            '';
+
+
+
+
+
+
+ +
+

3.3. Common Home-Manager

+
+
+
+

3.3.1. Installed packages

+
+

+Here are defined some packages that I would like to use across all my machines. Most of these should not require further setup. Notably the cura package is severely outdated on nixpkgs, so I just fetch a more recent AppImage and run that instead. +

+ +

+Also, I define some useful shell scripts here. +

+ +

+Programming languages and default lsp's are defined here: System Packages +

+
+ +
    +
  1. Packaged
    +
    +
    +
    +{ config, pkgs, lib, fetchFromGitHub , ... }:
    +
    +{
    +  home.packages = with pkgs; [
    +
    +    # audio stuff
    +    spek # spectrum analyzer
    +    losslessaudiochecker
    +    ffmpeg_5-full
    +    flac
    +    mediainfo
    +    picard
    +    audacity
    +    sox
    +
    +    # printing
    +    cups
    +    gnome.simple-scan
    +
    +    # dict
    +    (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ]))
    +
    +    # utilities
    +    util-linux
    +    nmap
    +    lsof
    +
    +    # local file sharing
    +    wormhole-rs
    +
    +    # b2 backup @backblaze
    +    restic
    +
    +    # "big" programs
    +    gimp
    +    inkscape
    +    zoom-us
    +    # nomacs
    +    libreoffice-qt
    +    xournalpp
    +    obsidian
    +    spotify
    +    discord
    +    nextcloud-client
    +    spotify-player
    +    element-desktop-wayland
    +    nicotine-plus
    +    transmission
    +    mktorrent
    +    hexchat
    +    hugo
    +
    +    # kyria
    +    qmk
    +    qmk-udev-rules
    +
    +    # games
    +    lutris
    +    wine
    +    libudev-zero
    +    dwarfs
    +    fuse-overlayfs
    +    # steam
    +    # steam-run
    +    patchelf
    +    gamescope
    +    vulkan-tools
    +    moonlight-qt
    +
    +    # firefox related
    +    tridactyl-native
    +
    +    # mako related
    +    mako
    +    libnotify
    +
    +    # general utilities
    +    unrar
    +    samba
    +    cifs-utils
    +    zbar # qr codes
    +    readline
    +    autotiling
    +    brightnessctl
    +    libappindicator-gtk3
    +    sqlite
    +    speechd
    +    networkmanagerapplet
    +    psmisc # kill etc
    +    lm_sensors
    +    # jq # used for searching the i3 tree in check<xxx>.sh files
    +
    +    # specifically needed for anki
    +    # mpv
    +    anki-bin
    +
    +    # dirvish file previews
    +    fd
    +    imagemagick
    +    poppler
    +    ffmpegthumbnailer
    +    mediainfo
    +    gnutar
    +    unzip
    +
    +    #nautilus
    +    gnome.nautilus
    +    xfce.tumbler
    +    libgsf
    +
    +    # wayland stuff
    +    wtype
    +    wl-clipboard
    +    wl-mirror
    +
    +    # screenshotting tools
    +    grim
    +    slurp
    +
    +    # the following packages are used (in some way) by waybar
    +    playerctl
    +    pavucontrol
    +    pamixer
    +    # gnome.gnome-clocks
    +    # wlogout
    +    # jdiskreport
    +    syncthingtray
    +    # monitor
    +
    +    #keychain
    +    qalculate-gtk
    +    gcr # needed for gnome-secrets to work
    +    gnome.seahorse
    +
    +    # sops-related
    +    sops
    +    ssh-to-age
    +
    +    # mail related packages
    +    mu
    +
    +    # latex and related packages
    +    (pkgs.texlive.combine {
    +      inherit (pkgs.texlive) scheme-full
    +        dvisvgm dvipng # for preview and export as html
    +        wrapfig amsmath ulem hyperref capt-of;
    +    })
    +
    +    # font stuff
    +    (nerdfonts.override { fonts = [ "FiraMono" "FiraCode" "NerdFontsSymbolsOnly"]; })
    +    noto-fonts-emoji
    +    font-awesome_5
    +    noto-fonts
    +    noto-fonts-cjk-sans
    +
    +
    +
    +
    +
  2. + +
  3. Self-defined
    +
    +
    +
    +    # cura
    +    (let cura5 = appimageTools.wrapType2 rec {
    +           name = "cura5";
    +           version = "5.4.0";
    +           src = fetchurl {
    +             url = "https://github.com/Ultimaker/Cura/releases/download/${version}/UltiMaker-Cura-${version}-linux-modern.AppImage";
    +             hash = "sha256-QVv7Wkfo082PH6n6rpsB79st2xK2+Np9ivBg/PYZd74=";
    +           };
    +           extraPkgs = pkgs: with pkgs; [ ];
    +         }; in writeScriptBin "cura" ''
    +          #! ${pkgs.bash}/bin/bash
    +          # AppImage version of Cura loses current working directory and treats all paths relateive to $HOME.
    +          # So we convert each of the files passed as argument to an absolute path.
    +          # This fixes use cases like `cd /path/to/my/files; cura mymodel.stl anothermodel.stl`.
    +          args=()
    +          for a in "$@"; do
    +              if [ -e "$a" ]; then
    +                 a="$(realpath "$a")"
    +              fi
    +              args+=("$a")
    +          done
    +          exec "${cura5}/bin/cura5" "''${args[@]}"
    +          '')
    +
    +    #E: hides scratchpad depending on state, calls emacsclient for edit and then restores the scratchpad state
    +    (pkgs.writeShellScriptBin "e" ''
    +       bash ~/.dotfiles/scripts/editor_nowait.sh "$@"
    +       '')
    +    (pkgs.writeShellScriptBin "timer" ''
    +       sleep "$1"; while true; do spd-say "$2"; sleep 0.5; done;
    +       '')
    +
    +    (pkgs.writeScriptBin "project" ''
    + #! ${pkgs.bash}/bin/bash
    + if [ "$1" == "rust" ]; then
    + cp ~/.dotfiles/templates/rust_flake.nix ./flake.nix
    + cp ~/.dotfiles/templates/toolchain.toml .
    + elif [ "$1" == "cpp" ]; then
    + cp ~/.dotfiles/templates/cpp_flake.nix ./flake.nix
    + elif [ "$1" == "python" ]; then
    + cp ~/.dotfiles/templates/py_flake.nix ./flake.nix
    + elif [ "$1" == "cuda" ]; then
    + cp ~/.dotfiles/templates/cu_flake.nix ./flake.nix
    + elif [ "$1" == "other" ]; then
    + cp ~/.dotfiles/templates/other_flake.nix ./flake.nix
    + elif [ "$1" == "latex" ]; then
    +   if [ "$2" == "" ]; then
    +   echo "No filename specified, usage: 'project latex <NAME>'"
    +   exit 0
    +   fi
    + cp ~/.dotfiles/templates/tex_standard.tex ./"$2".tex
    + exit 0
    + else
    + echo "No valid argument given. Valid arguments are rust cpp python, cuda"
    + exit 0
    + fi
    + echo "use flake" >> .envrc
    + direnv allow
    + '')
    +
    +    (pkgs.writeShellApplication {
    +      name = "pass-fuzzel";
    +      runtimeInputs = [ pkgs.pass pkgs.fuzzel ];
    +      text = ''
    +       shopt -s nullglob globstar
    +
    +       typeit=0
    +       if [[ $# -ge 1 && $1 == "--type" ]]; then
    +         typeit=1
    +         shift
    +       fi
    +
    +       export PASSWORD_STORE_DIR=~/.local/share/password-store
    +       prefix=''${PASSWORD_STORE_DIR-~/.local/share/password-store}
    +       password_files=( "$prefix"/**/*.gpg )
    +       password_files=( "''${password_files[@]#"$prefix"/}" )
    +       password_files=( "''${password_files[@]%.gpg}" )
    +
    +       password=$(printf '%s\n' "''${password_files[@]}" | fuzzel --dmenu "$@")
    +
    +       [[ -n $password ]] || exit
    +
    +       if [[ $typeit -eq 0 ]]; then
    +         pass show -c "$password" &>/tmp/pass-fuzzel
    +       else
    +         pass show "$password" | { IFS= read -r pass; printf %s "$pass"; } | wtype -
    +       fi
    +       notify-send -u critical -a pass -t 1000 "Copied/Typed Password"
    +     '';
    +    })
    +
    +    (pkgs.writeShellApplication {
    +      name = "pass-fuzzel-otp";
    +      runtimeInputs = [ pkgs.fuzzel (pkgs.pass.withExtensions (exts: [exts.pass-otp]))];
    +      text = ''
    +       shopt -s nullglob globstar
    +
    +       typeit=0
    +       if [[ $# -ge 1 && $1 == "--type" ]]; then
    +         typeit=1
    +         shift
    +       fi
    +
    +       export PASSWORD_STORE_DIR=~/.local/share/password-store
    +       prefix=''${PASSWORD_STORE_DIR-~/.local/share/password-store}
    +       password_files=( "$prefix"/otp/**/*.gpg )
    +       password_files=( "''${password_files[@]#"$prefix"/}" )
    +       password_files=( "''${password_files[@]%.gpg}" )
    +
    +       password=$(printf '%s\n' "''${password_files[@]}" | fuzzel --dmenu "$@")
    +
    +       [[ -n $password ]] || exit
    +
    +       if [[ $typeit -eq 0 ]]; then
    +         pass otp -c "$password" &>/tmp/pass-fuzzel
    +       else
    +         pass otp "$password" | { IFS= read -r pass; printf %s "$pass"; } | wtype -
    +       fi
    +       notify-send -u critical -a pass -t 1000 "Copied/Typed OTPassword"
    +     '';
    +    })
    +
    +    (pkgs.writeShellApplication {
    +      name = "cdw";
    +      runtimeInputs = [ pkgs.fzf ];
    +      text = ''
    +    cd "$(git worktree list | fzf | awk '{print $1}')"
    +    '';
    +    })
    +
    +    (pkgs.writeShellApplication {
    +      name = "cdb";
    +      runtimeInputs = [ pkgs.fzf ];
    +      text = ''
    +    git checkout "$(git branch --list | grep -v "^\*" | fzf | awk '{print $1}')"
    +    '';
    +    })
    +
    +    (pkgs.writeShellApplication {
    +      name = "bak";
    +      text = ''
    +      cp "$1"{,.bak}
    +    '';
    +    })
    +
    +];
    +
    +
    +
    +
    +
  4. +
+
+ +
+

3.3.2. sops

+
+

+I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine: +

+
    +
  • `ssh-keygen -t ed25519 -C "NAME sops"` in .ssh directory (or wherever) - name e.g. "sops"
  • +
  • cat ~/.ssh/sops.pub | ssh-to-age | wl-copy
  • +
  • add the output to .sops.yaml
  • +
  • cp ~/.ssh/sops.pub ~/.dotfiles/secrets/keys/NAME.pub
  • +
  • update entry for sops.age.sshKeyPaths
  • +
+ +
+
+sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml";
+sops.validateSopsFiles = false;
+
+# since we are using the home-manager implementation, we need to specify the runtime path for each secret
+sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";};
+sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";};
+sops.secrets.leon = {path = "/run/user/1000/secrets/leon";};
+sops.secrets.swarselmail = {path = "/run/user/1000/secrets/swarselmail";};
+sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";};
+
+
+
+
+ +
+

3.3.3. SSH Machines

+
+

+It is very convenient to have SSH aliases in place for machines that I use. This is mainly used for some server machines and some university clusters. +

+ +
+
+programs.ssh= {
+  enable = true;
+  extraConfig = "SetEnv TERM=xterm-256color";
+  matchBlocks = {
+    "nginx" = {
+      hostname = "192.168.1.14";
+      user = "root";
+    };
+    "jellyfin" = {
+      hostname = "192.168.1.16";
+      user = "root";
+    };
+    "pfsense" = {
+      hostname = "192.168.1.1";
+      user = "root";
+    };
+    "proxmox" = {
+      hostname = "192.168.1.2";
+      user = "root";
+    };
+    "transmission" = {
+      hostname = "192.168.1.6";
+      user = "root";
+    };
+    "fetcher" = {
+      hostname = "192.168.1.7";
+      user = "root";
+    };
+    "omv" = {
+      hostname = "192.168.1.3";
+      user = "root";
+    };
+    "webbot" = {
+      hostname = "192.168.1.11";
+      user = "root";
+    };
+    "nextcloud" = {
+      hostname = "192.168.1.5";
+      user = "root";
+    };
+    "sound" = {
+      hostname = "192.168.1.13";
+      user = "root";
+    };
+    "spotify" = {
+      hostname = "192.168.1.17";
+      user = "root";
+    };
+    "wordpress" = {
+      hostname = "192.168.1.9";
+      user = "root";
+    };
+    "turn" = {
+      hostname = "192.168.1.18";
+      user = "root";
+    };
+    "hugo" = {
+      hostname = "192.168.1.19";
+      user = "root";
+    };
+    "matrix" = {
+      hostname = "192.168.1.23";
+      user = "root";
+    };
+    "scroll" = {
+      hostname = "192.168.1.22";
+      user = "root";
+    };
+    "minecraft" = {
+      hostname = "130.61.119.129";
+      user = "opc";
+    };
+    "sync" = {
+      hostname = "193.122.53.173";
+      user = "root"; #this is a oracle vm server but needs root due to nixos-infect
+    };
+    "pkv" = {
+      hostname = "46.232.248.161";
+      user = "root";
+    };
+    "nebula" = {
+      hostname = "128.131.171.15";
+      user = "amp23s56";
+      compression = true;
+      identityFile = "~/.ssh/id_ed25519";
+      proxyCommand = "ssh -p 1022 -i ~/.ssh/id_ed25519 -q -W %h:%p %r@venus.par.tuwien.ac.at";
+      extraOptions = {
+        "TCPKeepAlive" = "yes";
+      };
+    };
+    "efficient" = {
+      hostname = "g0.complang.tuwien.ac.at";
+      forwardAgent = true;
+      user = "ep01427399";
+
+      # leaving the below lines in for future reference
+
+      # remoteForwards = [
+      #   {
+      #     bind.address = "/run/user/21217/gnupg/S.gpg-agent";
+      #     host.address = "/run/user/1000/gnupg/S.gpg-agent.extra";
+      #   }
+      #   {
+      #     bind.address = "/run/user/21217/gnupg/S.gpg-agent.ssh";
+      #     host.address = "/run/user/1000/gnupg/S.gpg-agent.ssh";
+      #   }
+      # ];
+      # extraOptions = {
+      # "RemoteForward" = "/run/user/21217/gnupg/S.gpg-agent /run/user/1000/gnupg/S.gpg-agent.extra";
+      # "StreamLocalBindUnlink" = "yes";
+      # "RemoteForward" = "/run/user/21217/gnupg/S.gpg-agent.ssh /run/user/1000/gnupg/S.gpg-agent.ssh";
+      # };
+      # setEnv = {
+      #   "TERM" = "xterm";
+      # };
+    };
+    "hydra" = {
+      hostname = "128.131.171.215";
+      user = "hpc23w33";
+      compression = true;
+      forwardAgent = true;
+      # identityFile = "~/.ssh/id_tuwien_hpc";
+      # proxyCommand = "ssh -p 1022 -i ~/.ssh/id_tuwien_hpc -q -W %h:%p %r@venus.par.tuwien.ac.at";
+      proxyCommand = "ssh -p 1022 -q -W %h:%p %r@venus.par.tuwien.ac.at";
+      extraOptions = {
+        "TCPKeepAlive" = "yes";
+      };
+    };
+  };
+};
+
+
+
+
+
+ +
+

3.3.4. Fonts + Theme

+
+

+These section allows home-manager to allow theme settings, and handles some other appearance-related settings like cursor styles. Interestingly, system icons (adwaita) still need to be setup on system-level, and will break if defined here. +

+ +

+This section has been notably empty ever since switching to stylix. Only Emacs is not allowed to be styled by it, because it becomes more ugly compared to my handcrafted setup. +

+ +
+
+stylix.targets.emacs.enable = false;
+
+
+
+
+
+ +
+

3.3.5. Desktop Entries

+
+

+Some programs lack a dmenu launcher - I define them myself here. +

+ +

+TODO: Non-NixOS machines (=sp3) should not use these by default, but instead the programs prefixed with "nixGL". I need to figure out how to automate this process, as it is not feasible to write desktop entries for all programs installed on that machine. +

+ +
+
+xdg.desktopEntries = {
+
+  cura = {
+    name = "Ultimaker Cura";
+    genericName = "Cura";
+    exec = "cura";
+    terminal = false;
+    categories = [ "Application"];
+  };
+
+  anki = {
+    name = "Anki Flashcards";
+    genericName = "Anki";
+    exec = "anki";
+    terminal = false;
+    categories = [ "Application"];
+  };
+
+  # schlidichat = {
+  #   name = "SchildiChat Matrix Client";
+  #   genericName = "SchildiChat";
+  #   exec = "schildichat-desktop -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";
+  #   terminal = false;
+  #   categories = [ "Application"];
+  # };
+
+  element = {
+    name = "Element Matrix Client";
+    genericName = "Element";
+    exec = "element-desktop -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";
+    terminal = false;
+    categories = [ "Application"];
+  };
+
+};
+
+
+
+
+
+ +
+

3.3.6. Linking dotfiles

+
+

+This section should be used in order to symlink already existing configuration files using `home.file` and setting session variables using `home.sessionVariables`. +

+ +

+As for the `home.sessionVariables`, it should be noted that environment variables that are needed at system start should NOT be loaded here, but instead in `programs.zsh.config.extraSessionCommands` (in the home-manager programs section). This is also where all the wayland related variables are stored. +

+ +
+
+home.file = {
+  "init.el" = {
+    source = ../../programs/emacs/init.el;
+    target = ".emacs.d/init.el";
+  };
+  "early-init.el" = {
+    source = ../../programs/emacs/early-init.el;
+    target = ".emacs.d/early-init.el";
+  };
+  # on NixOS, Emacs does not find the aspell dicts easily. Write the configuration manually
+  ".aspell.conf" = {
+    source = ../../programs/config/.aspell.conf;
+    target = ".aspell.conf";
+  };
+  ".gitmessage" = {
+    source = ../../programs/git/.gitmessage;
+    target = ".gitmessage";
+  };
+};
+
+
+
+
+
+ +
+

3.3.7. Sourcing environment variables

+
+
+
+home.sessionVariables = {
+  EDITOR = "bash ~/.dotfiles/scripts/editor.sh";
+};
+
+
+
+
+
+ +
+

3.3.8. Programs

+
+

+This houses the configurations for all programs managed by home-manager. +

+
+ +
    +
  1. General Programs: bottom, imv, sioyek, bat, carapace, wlogout, swayr, yt-dlp, mpv, jq, nix-index, ripgrep, pandoc, fzf
    +
    +

    +This section is for programs that require no further configuration +

    + +
    +
    +# zsh Integration is enabled by default for these
    +programs.bottom.enable = true;
    +programs.imv.enable = true;
    +programs.sioyek.enable = true;
    +programs.bat.enable = true;
    +programs.carapace.enable = true;
    +programs.wlogout.enable = true;
    +programs.swayr.enable = true;
    +programs.yt-dlp.enable = true;
    +programs.mpv.enable = true;
    +programs.jq.enable = true;
    +programs.nix-index.enable = true;
    +programs.ripgrep.enable = true;
    +programs.pandoc.enable = true;
    +programs.fzf.enable = true;
    +programs.zoxide.enable = true;
    +
    +
    +
    +
    +
  2. +
+
+ +
+

3.3.9. password-store

+
+

+Enables password store with the pass-otp extension which allows me to store and generate one-time-passwords. +

+ +
+
+programs.password-store = {
+  enable = true;
+  package = pkgs.pass.withExtensions (exts: [exts.pass-otp]);
+};
+
+
+
+
+
+ +
+

3.3.10. direnv

+
+

+Enables direnv, which I use for nearly all of my nix dev flakes. +

+ +
+
+programs.direnv = {
+  enable = true;
+  nix-direnv.enable = true;
+  };
+
+
+
+
+
+ +
+

3.3.11. eza

+
+

+Eza provides me with a better ls command and some other useful aliases. +

+ +
+
+programs.eza = {
+  enable = true;
+  icons = true;
+  git = true;
+  extraOptions = [
+    "-l"
+    "--group-directories-first"
+  ];
+};
+
+
+
+
+
+ +
+

3.3.12. git

+
+

+Here I set up my git config, automatic signing of commits, useful aliases for my ost used commands (for when I am not using Magit) as well as a git template defined in Linking dotfiles. +

+ +
+
+programs.git = {
+  enable = true;
+  aliases = {
+    a = "add";
+    c = "commit";
+    cl = "clone";
+    co = "checkout";
+    b = "branch";
+    i = "init";
+    m = "merge";
+    s = "status";
+    r = "restore";
+    p = "pull";
+    pp = "push";
+  };
+  signing = {
+    key = "0x76FD3810215AE097";
+    signByDefault = true;
+  };
+  userEmail = "leon.schwarzaeugl@gmail.com";
+  userName = "Swarsel";
+  difftastic.enable = true;
+  lfs.enable = true;
+  includes = [
+    {
+      contents = {
+        github = {
+          user = "Swarsel";
+        };
+        commit = {
+          template = "~/.gitmessage";
+        };
+      };
+    }
+  ];
+};
+
+
+
+
+
+ +
+

3.3.13. Fuzzel

+
+

+Here I only need to set basic layout options - the rest is being managed by stylix. +

+ +
+
+programs.fuzzel = {
+  enable = true;
+  settings = {
+    main = {
+      layer = "overlay";
+      lines = "10";
+      width = "40";
+    };
+    border.radius = "0";
+  };
+};
+
+
+
+
+
+ +
+

3.3.14. Starship

+
+

+Starship makes my zsh look cooler! I have symbols for most programming languages and toolchains, also I build my own powerline. +

+ +
+
+programs.starship = {
+  enable = true;
+  enableZshIntegration = true;
+  settings = {
+    add_newline = false;
+    format = "$character";
+    right_format = "$all";
+    command_timeout = 3000;
+
+    directory.substitutions = {
+      "Documents" = "󰈙 ";
+      "Downloads" = " ";
+      "Music" = " ";
+      "Pictures" = " ";
+    };
+
+    git_status = {
+      style = "bg:#394260";
+      format = "[[($all_status$ahead_behind )](fg:#769ff0 bg:#394260)]($style)";
+    };
+
+    character = {
+      success_symbol = "[λ](bold green)";
+      error_symbol = "[λ](bold red)";
+    };
+
+    aws.symbol = "  ";
+    buf.symbol = " ";
+    c.symbol = " ";
+    conda.symbol = " ";
+    dart.symbol = " ";
+    directory.read_only = " 󰌾";
+    docker_context.symbol = " ";
+    elixir.symbol = " ";
+    elm.symbol = " ";
+    fossil_branch.symbol = " ";
+    git_branch.symbol = " ";
+    golang.symbol = " ";
+    guix_shell.symbol = " ";
+    haskell.symbol = " ";
+    haxe.symbol = " ";
+    hg_branch.symbol = " ";
+    hostname.ssh_symbol = " ";
+    java.symbol = " ";
+    julia.symbol = " ";
+    lua.symbol = " ";
+    memory_usage.symbol = "󰍛 ";
+    meson.symbol = "󰔷 ";
+    nim.symbol = "󰆥 ";
+    nix_shell.symbol = " ";
+    nodejs.symbol = " ";
+
+    os.symbols = {
+      Alpaquita = " ";
+      Alpine = " ";
+      Amazon = " ";
+      Android = " ";
+      Arch = " ";
+      Artix = " ";
+      CentOS = " ";
+      Debian = " ";
+      DragonFly = " ";
+      Emscripten = " ";
+      EndeavourOS = " ";
+      Fedora = " ";
+      FreeBSD = " ";
+      Garuda = "󰛓 ";
+      Gentoo = " ";
+      HardenedBSD = "󰞌 ";
+      Illumos = "󰈸 ";
+      Linux = " ";
+      Mabox = " ";
+      Macos = " ";
+      Manjaro = " ";
+      Mariner = " ";
+      MidnightBSD = " ";
+      Mint = " ";
+      NetBSD = " ";
+      NixOS = " ";
+      OpenBSD = "󰈺 ";
+      openSUSE = " ";
+      OracleLinux = "󰌷 ";
+      Pop = " ";
+      Raspbian = " ";
+      Redhat = " ";
+      RedHatEnterprise = " ";
+      Redox = "󰀘 ";
+      Solus = "󰠳 ";
+      SUSE = " ";
+      Ubuntu = " ";
+      Unknown = " ";
+      Windows = "󰍲 ";
+    };
+
+    package.symbol = "󰏗 ";
+    pijul_channel.symbol = " ";
+    python.symbol = " ";
+    rlang.symbol = "󰟔 ";
+    ruby.symbol = " ";
+    rust.symbol = " ";
+    scala.symbol = " ";
+  };
+};
+
+
+
+
+
+ +
+

3.3.15. Kitty

+
+

+Kitty is the terminal emulator of choice for me, it is nice to configure using nix, fast, and has a nice style. +

+ +

+The theme is handled by stylix. +

+ +
+
+programs.kitty = {
+  enable = true;
+  keybindings = {
+    "ctrl+shift+left" = "no_op";
+    "ctrl+shift+right" = "no_op";
+    "ctrl+shift+home" = "no_op";
+    "ctrl+shift+end" = "no_op";
+  };
+};
+
+
+
+
+
+ +
+

3.3.16. zsh

+
+

+zsh is the most convenient shell for me and it happens to be super neat to configure within home manager. +

+ +

+Here we set some aliases (some of them should be shellApplications instead) as well as some zsh plugins like fzf-tab. +

+ +
+
+programs.zsh = {
+  enable = true;
+  shellAliases = {
+    hg = "history | grep";
+    hmswitch = "cd ~/.dotfiles; home-manager --flake .#$(whoami)@$(hostname) switch; cd -;";
+    nswitch = "cd ~/.dotfiles; sudo nixos-rebuild --flake .#$(hostname) switch; cd -;";
+    edithome = "bash ~/.dotfiles/scripts/editor.sh ~/.dotfiles/Nix.org";
+    magit = "emacsclient -nc -e \"(magit-status)\"";
+    config="git --git-dir=$HOME/.cfg/ --work-tree=$HOME";
+    g="git";
+    c="git --git-dir=$HOME/.dotfiles/.git --work-tree=$HOME/.dotfiles/";
+    passpush = "cd ~/.local/share/password-store; git add .; git commit -m 'pass file changes'; git push; cd -;";
+    passpull = "cd ~/.local/share/password-store; git pull; cd -;";
+    hotspot = "nmcli connection up local; nmcli device wifi hotspot password 12345678;";
+    cd="z";
+    cdr = "cd \"$( (find /home/swarsel/Documents/GitHub -maxdepth 1 && echo /home/swarsel/.dotfiles) | fzf )\"";
+  };
+  autosuggestion.enable = true;
+  enableCompletion = true;
+  syntaxHighlighting.enable = true;
+  autocd = false;
+  cdpath = [
+    "~/.dotfiles"
+    # "~/Documents/GitHub"
+  ];
+  defaultKeymap = "emacs";
+  dirHashes = {
+    dl    = "$HOME/Downloads";
+    gh    = "$HOME/Documents/GitHub";
+  };
+  history = {
+    expireDuplicatesFirst = true;
+    path = "$HOME/.histfile";
+    save = 10000;
+    size = 10000;
+  };
+  historySubstringSearch.enable = true;
+  plugins = [
+    {
+      name = "fzf-tab";
+      src = pkgs.zsh-fzf-tab;
+    }
+  ];
+  initExtra = ''
+    bindkey "^[[1;5D" backward-word
+    bindkey "^[[1;5C" forward-word
+  '';
+};
+
+
+
+
+
+ +
+

3.3.17. Mail

+
+

+Normally I use 4 mail accounts - here I set them all up. Three of them are Google accounts (sadly), which are a chore to setup. The last is just a sender account that I setup SMTP for here. +

+ +
+
+programs.mbsync = {
+  enable = true;
+};
+# this is needed so that mbsync can use the passwords from sops
+systemd.user.services.mbsync.Unit.After = [ "sops-nix.service" ];
+
+programs.msmtp = {
+  enable = true;
+};
+
+programs.mu = {
+  enable = true;
+};
+
+accounts.email = {
+  maildirBasePath = "Mail";
+  accounts.leon = {
+    primary = true;
+    address = "leon.schwarzaeugl@gmail.com";
+    userName = "leon.schwarzaeugl@gmail.com";
+    realName = "Leon Schwarzäugl";
+    passwordCommand = "cat ${config.sops.secrets.leon.path}";
+    # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/leon.schwarzaeugl@gmail.com.gpg";
+    gpg = {
+      key = "0x76FD3810215AE097";
+      signByDefault = true;
+    };
+    imap.host = "imap.gmail.com";
+    smtp.host = "smtp.gmail.com";
+    mu.enable = true;
+    msmtp = {
+      enable = true;
+    };
+    mbsync = {
+      enable = true;
+      create= "maildir";
+      expunge = "both";
+      patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
+      extraConfig = {
+        channel = {
+          Sync = "All";
+        };
+        account = {
+          Timeout = 120;
+          PipelineDepth = 1;
+        };
+      };
+    };
+  };
+
+  accounts.swarsel = {
+    address = "leon@swarsel.win";
+    userName = "8227dc594dd515ce232eda1471cb9a19";
+    realName = "Leon Schwarzäugl";
+    passwordCommand = "cat ${config.sops.secrets.swarselmail.path}";
+    smtp = {
+      host = "in-v3.mailjet.com";
+      port = 587;
+      tls = {
+        enable = true;
+        useStartTls = true;
+      };
+    };
+    mu.enable = false;
+    msmtp = {
+      enable = true;
+    };
+    mbsync = {
+      enable = false;
+      };
+  };
+
+  accounts.nautilus = {
+    primary = false;
+    address = "nautilus.dw@gmail.com";
+    userName = "nautilus.dw@gmail.com";
+    realName = "Nautilus";
+    passwordCommand = "cat ${config.sops.secrets.nautilus.path}";
+    # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/nautilus.dw@gmail.com.gpg";
+    imap.host = "imap.gmail.com";
+    smtp.host = "smtp.gmail.com";
+    msmtp.enable = true;
+    mu.enable = true;
+    mbsync = {
+      enable = true;
+      create= "maildir";
+      expunge = "both";
+      patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
+      extraConfig = {
+        channel = {
+          Sync = "All";
+        };
+        account = {
+          Timeout = 120;
+          PipelineDepth = 1;
+        };
+      };
+    };
+  };
+  accounts.mrswarsel = {
+    primary = false;
+    address = "mrswarsel@gmail.com";
+    userName = "mrswarsel@gmail.com";
+    realName = "Swarsel";
+    # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/mrswarsel@gmail.com.gpg";
+    passwordCommand = "cat ${config.sops.secrets.mrswarsel.path}";
+    imap.host = "imap.gmail.com";
+    smtp.host = "smtp.gmail.com";
+    msmtp.enable = true;
+    mu.enable = true;
+    mbsync = {
+      enable = true;
+      create= "maildir";
+      expunge = "both";
+      patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
+      extraConfig = {
+        channel = {
+          Sync = "All";
+        };
+        account = {
+          Timeout = 120;
+          PipelineDepth = 1;
+        };
+      };
+    };
+  };
+};
+
+
+
+
+
+ +
+

3.3.18. Home-manager: Emacs

+
+

+By using the emacs-overlay NixOS module, I can install all Emacs packages that I want to use right through NixOS. This is done by passing my init.el file to the configuration which will then be parsed upon system rebuild, looking for use-package sections in the Elisp code. Also I define here the style of Emacs that I want to run - I am going with native Wayland Emacs here (emacs-pgtk). All of the nice options such as tree-sitter support are enabled by default, so I do not need to adjust the build process. +

+ +

+Lastly, I am defining some more packages here that the parser has problems finding. Also there are some packages that are not in ELPA or MELPA that I still want to use, like calfw and fast-scroll, so I build them here. +

+ +
+
+# enable emacs overlay for bleeding edge features
+# also read init.el file and install use-package packages
+programs.emacs = {
+  enable = true;
+  package = (pkgs.emacsWithPackagesFromUsePackage {
+    config = ../../programs/emacs/init.el;
+    package = pkgs.emacs-pgtk;
+    alwaysEnsure = true;
+    alwaysTangle = true;
+    extraEmacsPackages = epkgs: [
+      epkgs.mu4e
+      epkgs.use-package
+      epkgs.lsp-bridge
+      epkgs.doom-themes
+
+      # build the rest of the packages myself
+      # org-calfw is severely outdated on MELPA and throws many warnings on emacs startup
+      # build the package from the haji-ali fork, which is well-maintained
+       (epkgs.trivialBuild rec {
+        pname = "calfw";
+        version = "1.0.0-20231002";
+        src = pkgs.fetchFromGitHub {
+          owner = "haji-ali";
+          repo = "emacs-calfw";
+          rev = "bc99afee611690f85f0cd0bd33300f3385ddd3d3";
+          hash = "sha256-0xMII1KJhTBgQ57tXJks0ZFYMXIanrOl9XyqVmu7a7Y=";
+        };
+        packageRequires = [ epkgs.howm ];
+      })
+
+       (epkgs.trivialBuild rec {
+        pname = "fast-scroll";
+        version = "1.0.0-20191016";
+        src = pkgs.fetchFromGitHub {
+          owner = "ahungry";
+          repo = "fast-scroll";
+          rev = "3f6ca0d5556fe9795b74714304564f2295dcfa24";
+          hash = "sha256-w1wmJW7YwXyjvXJOWdN2+k+QmhXr4IflES/c2bCX3CI=";
+        };
+        packageRequires = [];
+      })
+
+    ];
+  });
+};
+
+
+
+
+
+ +
+

3.3.19. Waybar

+
+

+Again I am just using the first bar option here that I was able to find good understandable documentation for. Of note is that the `cpu` section's `format` is not defined here, but in section 1 (since not every machine has the same number of cores) +

+ +

+The rest of this configuration is found here: +

+ + +
+
+programs.waybar = {
+
+  enable = true;
+  # systemd.enable = true;
+  settings = {
+    mainBar = {
+      layer = "top";
+      position = "top";
+      modules-left = [ "sway/workspaces" "custom/outer-right-arrow-dark" "sway/window"];
+      modules-center = [ "sway/mode" "custom/configwarn" ];
+      "sway/mode" = {
+        format = "<span style=\"italic\" font-weight=\"bold\">{}</span>";
+      };
+
+      "custom/configwarn" = {
+        exec= "bash ~/.dotfiles/scripts/checkconfigstatus.sh";
+        interval= 60;
+      };
+
+      "group/hardware" = {
+        orientation = "inherit";
+        drawer = {
+          "transition-left-to-right" = false;
+        };
+        modules = [
+          "tray"
+          "temperature"
+          "power-profiles-daemon"
+          "custom/left-arrow-light"
+          "disk"
+          "custom/left-arrow-dark"
+          "memory"
+          "custom/left-arrow-light"
+          "cpu"
+          "custom/left-arrow-dark"
+        ];
+      };
+
+      power-profiles-daemon = {
+        format= "{icon}";
+        tooltip-format= "Power profile: {profile}\nDriver: {driver}";
+        tooltip= true;
+        format-icons= {
+          "default"= "";
+          "performance"= "";
+          "balanced"= "";
+          "power-saver"= "";
+        };
+      };
+
+      temperature = {
+        critical-threshold = 80;
+        format-critical = " {temperatureC}°C";
+        format = " {temperatureC}°C";
+
+      };
+
+      mpris = {
+        format= "{player_icon} {title} <small>[{position}/{length}]</small>";
+        format-paused=  "{player_icon}  <i>{title} <small>[{position}/{length}]</small></i>";
+        player-icons=  {
+          "default" = "▶ ";
+          "mpv" = "🎵 ";
+          "spotify" = " ";
+        };
+        status-icons= {
+          "paused"= " ";
+        };
+        interval = 1;
+        title-len = 20;
+        artist-len = 20;
+        album-len = 10;
+      };
+      "custom/left-arrow-dark" = {
+        format = "";
+        tooltip = false;
+      };
+      "custom/outer-left-arrow-dark"= {
+        format = "";
+        tooltip = false;
+      };
+      "custom/left-arrow-light"= {
+        format= "";
+        tooltip= false;
+      };
+      "custom/right-arrow-dark"= {
+        format= "";
+        tooltip= false;
+      };
+      "custom/outer-right-arrow-dark"= {
+        format= "";
+        tooltip= false;
+      };
+      "custom/right-arrow-light"= {
+        format= "";
+        tooltip= false;
+      };
+      "sway/workspaces"= {
+        disable-scroll= true;
+        format= "{name}";
+      };
+
+      "clock#1"= {
+        min-length= 8;
+        interval= 1;
+        format= "{:%H:%M:%S}";
+        # on-click-right= "gnome-clocks";
+        tooltip-format= "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
+      };
+
+      "clock#2"= {
+        format= "{:%d. %B %Y}";
+        # on-click-right= "gnome-clocks";
+        tooltip-format= "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
+      };
+
+
+      pulseaudio= {
+        format= "{icon} {volume:2}%";
+        format-bluetooth= "{icon} {volume}%";
+        format-muted= "MUTE";
+        format-icons= {
+          headphones= "";
+          default= [
+            ""
+            ""
+          ];
+        };
+        scroll-step= 1;
+        on-click= "pamixer -t";
+        on-click-right= "pavucontrol";
+      };
+      memory= {
+        interval= 5;
+        format= " {}%";
+        tooltip-format= "Memory: {used:0.1f}G/{total:0.1f}G\nSwap: {swapUsed}G/{swapTotal}G";
+      };
+      cpu= {
+        min-length= 6;
+        interval= 5;
+        format-icons = ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█"];
+        # on-click-right= "com.github.stsdc.monitor";
+        on-click-right= "kitty -o confirm_os_window_close=0 btm";
+
+      };
+      battery= {
+        states= {
+          "warning"= 60;
+          "error"= 30;
+          "critical"= 15;
+        };
+        interval=5;
+        format= "{icon} {capacity}%";
+        format-charging= "{capacity}% ";
+        format-plugged= "{capacity}% ";
+        format-icons= [
+          ""
+          ""
+          ""
+          ""
+          ""
+        ];
+        on-click-right= "wlogout -p layer-shell";
+      };
+      disk= {
+        interval= 30;
+        format= "Disk {percentage_used:2}%";
+        path= "/";
+        states= {
+          "warning"= 80;
+          "critical"= 90;
+        };
+        tooltip-format = "{used} used out of {total} on {path} ({percentage_used}%)\n{free} free on {path} ({percentage_free}%)";
+      };
+      tray= {
+        icon-size= 20;
+      };
+      network= {
+        interval = 5;
+        format-wifi= "{signalStrength}% ";
+        format-ethernet= "";
+        format-linked= "{ifname} (No IP) ";
+        format-disconnected= "Disconnected ⚠";
+        format-alt= "{ifname}: {ipaddr}/{cidr}";
+        tooltip-format-ethernet= "{ifname} via {gwaddr}: {essid} {ipaddr}/{cidr}\n\n⇡{bandwidthUpBytes} ⇣{bandwidthDownBytes}";
+        tooltip-format-wifi= "{ifname} via {gwaddr}: {essid} {ipaddr}/{cidr} \n{signaldBm}dBm @ {frequency}MHz\n\n⇡{bandwidthUpBytes} ⇣{bandwidthDownBytes}";
+      };
+    };
+  };
+
+  style = ''
+  @define-color foreground #fdf6e3;
+  @define-color background #1a1a1a;
+  @define-color background-alt #292b2e;
+  @define-color foreground-warning #268bd2;
+  @define-color background-warning @background;
+  @define-color foreground-error red;
+  @define-color background-error @background;
+  @define-color foreground-critical gold;
+  @define-color background-critical blue;
+
+
+  * {
+      border: none;
+      border-radius: 0;
+      font-family: "FiraCode Nerd Font Propo", "Font Awesome 5 Free";
+      font-size: 14px;
+      min-height: 0;
+      margin: -1px 0px;
+  }
+
+  window#waybar {
+          background: transparent;
+          color: @foreground;
+          transition-duration: .5s;
+  }
+
+  window#waybar.hidden {
+      opacity: 0.2;
+  }
+
+
+  #mpris {
+      padding: 0 10px;
+      background-color: transparent;
+      color: #1DB954;
+      font-family: Monospace;
+      font-size: 12px;
+  }
+
+  #custom-right-arrow-dark,
+  #custom-left-arrow-dark {
+          color: @background;
+          background: @background-alt;
+          font-size: 24px;
+  }
+
+  #window {
+          font-size: 12px;
+          padding: 0 20px;
+  }
+
+  #mode {
+      background: @background-critical;
+      color: @foreground-critical;
+      padding: 0 3px;
+  }
+
+  #custom-configwarn {
+      color: black;
+      padding: 0 3px;
+      animation-name: configblink;
+      animation-duration: 0.5s;
+      animation-timing-function: linear;
+      animation-iteration-count: infinite;
+      animation-direction: alternate;
+  }
+
+  #custom-outer-right-arrow-dark,
+  #custom-outer-left-arrow-dark {
+          color: @background;
+          font-size: 24px;
+  }
+
+  #custom-outer-left-arrow-dark,
+  #custom-left-arrow-dark,
+  #custom-left-arrow-light {
+          margin: 0 -1px;
+  }
+
+  #custom-right-arrow-light,
+  #custom-left-arrow-light {
+          color: @background-alt;
+          background: @background;
+          font-size: 24px;
+  }
+
+  #workspaces,
+  #clock.1,
+  #clock.2,
+  #clock.3,
+  #pulseaudio,
+  #memory,
+  #cpu,
+  #temperature,
+  #power-profiles-daemon,
+  #mpris,
+  #tray {
+          background: @background;
+  }
+
+  #network,
+  #clock.2,
+  #battery,
+  #cpu,
+  #custom-pseudobat,
+  #disk {
+          background: @background-alt;
+  }
+
+
+  #workspaces button {
+          padding: 0 2px;
+          color: #fdf6e3;
+  }
+  #workspaces button.focused {
+          color: @foreground-warning;
+  }
+
+  #workspaces button:hover {
+      background: @foreground;
+      color: @background;
+          border: @foreground;
+          padding: 0 2px;
+          box-shadow: inherit;
+          text-shadow: inherit;
+  }
+
+  #workspaces button.urgent {
+      color: @background-critical;
+      background: @foreground-critical;
+  }
+
+  #network {
+      color: #cc99c9;
+  }
+
+  #temperature,
+  #power-profiles-daemon {
+      color: #9ec1cf;
+  }
+
+  #disk {
+      /*color: #b58900;*/
+      color: #9ee09e;
+  }
+
+  #disk.warning {
+      color:            @foreground-error;
+      background-color: @background-error;
+  }
+  #disk.critical,
+  #temperature.critical {
+      color:            @foreground-critical;
+      background-color: @background-critical;
+      animation-name: blink;
+      animation-duration: 0.5s;
+      animation-timing-function: linear;
+      animation-iteration-count: infinite;
+      animation-direction: alternate;
+  }
+  #pulseaudio.muted {
+      color: @foreground-error;
+  }
+  #memory {
+          /*color: #2aa198;*/
+          color: #fdfd97;
+  }
+  #cpu {
+      /*color: #6c71c4;*/
+      color: #feb144;
+  }
+
+  #pulseaudio {
+      /*color: #268bd2;*/
+      color: #ff6663;
+  }
+
+  #battery,
+  #custom-pseudobat {
+          color: cyan;
+  }
+  #battery.discharging {
+      color:      #859900;
+  }
+
+  @keyframes blink {
+      to {
+          color:            @foreground-error;
+          background-color: @background-error;
+      }
+  }
+  @keyframes configblink {
+      to {
+          color:            @foreground-error;
+          background-color: transparent;
+      }
+  }
+
+  #battery.critical:not(.charging) {
+      color:            @foreground-critical;
+      background-color: @background-critical;
+      animation-name: blink;
+      animation-duration: 0.5s;
+      animation-timing-function: linear;
+      animation-iteration-count: infinite;
+      animation-direction: alternate;
+  }
+
+  #clock.1,
+  #clock.2,
+  #clock.3 {
+      font-family: Monospace;
+  }
+
+  #clock,
+  #pulseaudio,
+  #memory,
+  #cpu,
+  #tray,
+  #temperature,
+  #power-profiles-daemon,
+  #network,
+  #mpris,
+  #battery,
+  #custom-pseudobat,
+  #disk {
+          padding: 0 3px;
+  }
+      '';
+};
+
+
+
+
+
+ +
+

3.3.20. Firefox

+
+

+Setting up firefox along with some policies that are important to me (mostlu disabling telemetry related stuff as well as Pocket). I also enable some integrations that enable super useful packages, namely tridactyl and browserpass. +

+ +

+Also, using NUR with rycee's firefox addons, it is very convenient for me to add firefox addons here that will be automatically installed. +

+ +

+Also, I setup some search aliases for functions I often use, such as NixOS options search (@no) +

+ +

+I used to build the firefox addon bypass-paywalls-clean myself here, but the maintainer always deletes old packages, and it became a chore for me to maintain here, so I no longer do that. +

+ +
+
+programs.firefox = {
+  enable = true;
+  package = pkgs.firefox.override {
+    nativeMessagingHosts = [
+      pkgs.tridactyl-native
+      pkgs.browserpass
+      pkgs.plasma5Packages.plasma-browser-integration
+    ];
+  };
+  policies = {
+      CaptivePortal = false;
+      DisableFirefoxStudies = true;
+      DisablePocket = true;
+      DisableTelemetry = true;
+      DisableFirefoxAccounts = false;
+      NoDefaultBookmarks = true;
+      OfferToSaveLogins = false;
+      OfferToSaveLoginsDefault = false;
+      EnableTrackingProtection = true;
+      };
+  profiles.default = {
+    isDefault = true;
+    extensions = with pkgs.nur.repos.rycee.firefox-addons; [
+      tridactyl
+      browserpass
+      clearurls
+      darkreader
+      enhancer-for-youtube
+      istilldontcareaboutcookies
+      translate-web-pages
+      ublock-origin
+      reddit-enhancement-suite
+      pushbullet
+      sponsorblock
+      web-archives
+      single-file
+      widegithub
+      enhanced-github
+      unpaywall
+      # fastforwardteam
+      don-t-fuck-with-paste
+      plasma-integration
+
+  #     (let version = "3.4.5.0";
+  #                             in buildFirefoxXpiAddon {
+  # pname = "bypass-paywalls-clean";
+  # inherit version;
+  # addonId = "magnolia@12.34";
+  # url =
+  #   "https://gitlab.com/magnolia1234/bpc-uploads/-/raw/master/bypass_paywalls_clean-3.4.5.0.xpi";
+  # sha256 = "703d30c15b88291bd0305cc59013693aea5f75a40ea98fb8e252d1c7bfb43514";
+  # meta = with lib; {
+  #   homepage =
+  #     "https://gitlab.com/magnolia1234/bypass-paywalls-firefox-clean";
+  #   description = "Bypass Paywalls of (custom) news sites";
+  #   license = licenses.mit;
+  #   platforms = platforms.all;
+  # };
+  # })
+
+      (buildFirefoxXpiAddon {
+        pname = ":emoji:";
+        version = "0.1.3";
+        addonId = "gonelf@gmail.com";
+        url = "https://addons.mozilla.org/firefox/downloads/file/3365324/emojidots-0.1.3.xpi";
+        sha256 = "4f7cc25c478fe52eb82f37c9ff4978dcaa3f95020398c5b184e517f6efa2c201";
+        meta = with lib;
+          {
+            description = "emoji autocomplete anywhere on the internet";
+            mozPermissions = [ "https://gist.githubusercontent.com/gonelf/d8ae3ccb7902b501c4a5dd625d4089da/raw/5eeda197ba92f8c8139e846a1225d5640077e06f/emoji_pretty.json" "tabs" "storage"];
+            platforms = platforms.all;
+          };
+      })
+
+    ];
+    search.engines = {
+      "Nix Packages" = {
+        urls = [{
+          template = "https://search.nixos.org/packages";
+          params = [
+            { name = "type"; value = "packages"; }
+            { name = "query"; value = "{searchTerms}"; }
+          ];
+        }];
+        icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+        definedAliases = [ "@np" ];
+      };
+
+      "NixOS Wiki" = {
+        urls = [{
+          template = "https://nixos.wiki/index.php?search={searchTerms}";
+        }];
+        iconUpdateURL = "https://nixos.wiki/favicon.png";
+        updateInterval = 24 * 60 * 60 * 1000; # every day
+        definedAliases = [ "@nw" ];
+      };
+
+      "NixOS Options" = {
+        urls = [{
+          template = "https://search.nixos.org/options";
+          params = [
+            { name = "query"; value = "{searchTerms}"; }
+          ];
+        }];
+
+        icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+        definedAliases = [ "@no" ];
+      };
+
+      "Home Manager Options" = {
+        urls = [{ template = "https://home-manager-options.extranix.com/";
+                  params = [
+                    { name = "query"; value = "{searchTerms}"; }
+                  ];
+                }];
+
+        icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+        definedAliases = [ "@hm" "@ho" "@hmo" ];
+      };
+
+      "Google".metaData.alias = "@g";
+    };
+    search.force = true; # this is required because otherwise the search.json.mozlz4 symlink gets replaced on every firefox restart
+  };
+};
+
+
+
+
+
+ +
+

3.3.21. Services

+
+

+Services that can be defined through home-manager should be defined here. +

+
+ +
    +
  1. gnome-keyring
    +
    +

    +Used for storing sessions in e.g. Nextcloud +

    + +
    +
    +services.gnome-keyring = {
    +  enable = true;
    +};
    +
    +
    +
    +
    +
  2. + +
  3. mbsync
    +
    +

    +Used for syncing mail. This might be automatically enabled by my mail configuration, but I like to make sure. +

    + +
    +
    +services.mbsync = {
    +  enable = true;
    +};
    +
    +
    +
    +
    +
  4. + +
  5. KDE Connect
    +
    +

    +This enables phone/computer communication, including sending clipboard, files etc. Sadly on Wayland many of the features are broken (like remote control). +

    + +
    +
    +services.kdeconnect = {
    +  enable = true;
    +  indicator = true;
    +};
    +
    +
    +
    +
    +
  6. + +
  7. syncthing
    +
    +

    +Enables the syncthing service which talks to my syncthing instance on the Oracle cloud as well as my home server. +

    + +
    +
    +services.syncthing = {
    +  enable = true;
    +  tray = {
    +    enable = false; # we enable this by installing the syncthingtray package instead, it works better.
    +  };
    +};
    +
    +
    +
    +
    +
  8. + +
  9. Emacs server
    +
    +

    +This enables the Emacs server. This allows me to invocate Emacs by using emacsclient instead, which is a lot faster. +

    + +
    +
    +services.emacs = {
    +  enable = true;
    +  # socketActivation.enable = false;
    +  # startWithUserSession = "graphical";
    +};
    +
    +
    +
    +
  10. + +
  11. Mako
    +
    +

    +Desktop notifications! +

    + +

    +The `extraConfig` section here CANNOT be reindented. This has something to do with how nix handles multiline strings, when indented Mako will fail to start. This might be a mako bug as well. +

    + +
    +
    +services.mako = {
    +  enable = true;
    +  # backgroundColor = "#2e3440";
    +  # borderColor = "#88c0d0";
    +  borderRadius = 15;
    +  borderSize = 1;
    +  defaultTimeout = 5000;
    +  height = 150;
    +  icons = true;
    +  ignoreTimeout = true;
    +  layer = "overlay";
    +  maxIconSize = 64;
    +  sort = "-time";
    +  width = 300;
    +  # font = "monospace 10";
    +  extraConfig = "[urgency=low]
    +border-color=#cccccc
    +[urgency=normal]
    +border-color=#d08770
    +[urgency=high]
    +border-color=#bf616a
    +default-timeout=3000
    +[category=mpd]
    +default-timeout=2000
    +group-by=category
    +";
    +};
    +
    +
    +
    +
    +
  12. +
+
+ +
+

3.3.22. Sway

+
+

+I am currently using SwayFX, which adds some nice effects to sway, like rounded corners and hiding the separator between title and content of a window. +

+ +

+Currently, I am too lazy to explain every option here, but most of it is very self-explaining in any case. +

+ +
+
+wayland.windowManager.sway = {
+  enable = true;
+  checkConfig = false; # delete this line once SwayFX is fixed upstream
+  package = pkgs.swayfx;
+  # package = pkgs.sway;
+  systemd.enable = true;
+  systemd.xdgAutostart = true;
+  wrapperFeatures.gtk = true;
+  config = rec {
+    modifier = "Mod4";
+    terminal = "kitty";
+    menu = "fuzzel";
+    bars = [{ command = "waybar";}];
+    keybindings = let
+      modifier = config.wayland.windowManager.sway.config.modifier;
+    in {
+      "${modifier}+q" = "kill";
+      "${modifier}+f" = "exec firefox";
+      "${modifier}+Space" = "exec fuzzel";
+      "${modifier}+Shift+Space" = "floating toggle";
+      "${modifier}+e" = "exec emacsclient -nquc -a emacs -e \"(dashboard-open)\"";
+      "${modifier}+Shift+m" = "exec emacsclient -nquc -a emacs -e \"(mu4e)\"";
+      "${modifier}+Shift+c" = "exec emacsclient -nquc -a emacs -e \"(swarsel/open-calendar)\"";
+      "${modifier}+Shift+s" = "exec \"bash ~/.dotfiles/scripts/checkspotify.sh\"";
+      "${modifier}+m" = "exec \"bash ~/.dotfiles/scripts/checkspotifytui.sh\"";
+      "${modifier}+x" = "exec \"bash ~/.dotfiles/scripts/checkkitty.sh\"";
+      "${modifier}+d" = "exec \"bash ~/.dotfiles/scripts/checkdiscord.sh\"";
+      "${modifier}+Shift+r" = "exec \"bash ~/.dotfiles/scripts/restart.sh\"";
+      "${modifier}+Shift+t" = "exec \"bash ~/.dotfiles/scripts/toggle_opacity.sh\"";
+      "${modifier}+Shift+F12" = "move scratchpad";
+      "${modifier}+F12" = "scratchpad show";
+      "${modifier}+c" = "exec qalculate-gtk";
+      "${modifier}+p" = "exec pass-fuzzel";
+      "${modifier}+o" = "exec pass-fuzzel-otp";
+      "${modifier}+Shift+p" = "exec pass-fuzzel --type";
+      "${modifier}+Shift+o" = "exec pass-fuzzel-otp --type";
+      "${modifier}+Escape" = "mode $exit";
+      # "${modifier}+Shift+Escape" = "exec com.github.stsdc.monitor";
+      "${modifier}+Shift+Escape" = "exec kitty -o confirm_os_window_close=0 btm";
+      "${modifier}+s" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
+      "${modifier}+i" = "exec \"bash ~/.dotfiles/scripts/startup.sh\"";
+      "${modifier}+1" = "workspace 1:一";
+      "${modifier}+Shift+1" = "move container to workspace 1:一";
+      "${modifier}+2" = "workspace 2:二";
+      "${modifier}+Shift+2" = "move container to workspace 2:二";
+      "${modifier}+3" = "workspace 3:三";
+      "${modifier}+Shift+3" = "move container to workspace 3:三";
+      "${modifier}+4" = "workspace 4:四";
+      "${modifier}+Shift+4" = "move container to workspace 4:四";
+      "${modifier}+5" = "workspace 5:五";
+      "${modifier}+Shift+5" = "move container to workspace 5:五";
+      "${modifier}+6" = "workspace 6:六";
+      "${modifier}+Shift+6" = "move container to workspace 6:六";
+      "${modifier}+7" = "workspace 7:七";
+      "${modifier}+Shift+7" = "move container to workspace 7:七";
+      "${modifier}+8" = "workspace 8:八";
+      "${modifier}+Shift+8" = "move container to workspace 8:八";
+      "${modifier}+9" = "workspace 9:九";
+      "${modifier}+Shift+9" = "move container to workspace 9:九";
+      "${modifier}+0" = "workspace 10:十";
+      "${modifier}+Shift+0" = "move container to workspace 10:十";
+      "XF86AudioRaiseVolume" = "exec pactl set-sink-volume @DEFAULT_SINK@ +5%";
+      "XF86AudioLowerVolume" = "exec pactl set-sink-volume @DEFAULT_SINK@ -5%";
+      "${modifier}+Left" = "focus left";
+      "${modifier}+Right" = "focus right";
+      "${modifier}+Down" = "focus down";
+      "${modifier}+Up" = "focus up";
+      "${modifier}+Shift+Left" = "move left 40px";
+      "${modifier}+Shift+Right" = "move right 40px";
+      "${modifier}+Shift+Down" = "move down 40px";
+      "${modifier}+Shift+Up" = "move up 40px";
+      "${modifier}+h" = "focus left";
+      "${modifier}+l" = "focus right";
+      "${modifier}+j" = "focus down";
+      "${modifier}+k" = "focus up";
+      "${modifier}+Shift+h" = "move left 40px";
+      "${modifier}+Shift+l" = "move right 40px";
+      "${modifier}+Shift+j" = "move down 40px";
+      "${modifier}+Shift+k" = "move up 40px";
+      "${modifier}+Ctrl+Shift+c" = "reload";
+      "${modifier}+Shift+e" = "exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
+      "${modifier}+r" = "mode resize";
+      "${modifier}+Return" = "exec kitty";
+    };
+    modes = {
+      resize = {
+        Down = "resize grow height 10 px or 10 ppt";
+        Escape = "mode default";
+        Left = "resize shrink width 10 px or 10 ppt";
+        Return = "mode default";
+        Right = "resize grow width 10 px or 10 ppt";
+        Up = "resize shrink height 10 px or 10 ppt";
+      };
+    };
+    defaultWorkspace = "workspace 1:一";
+    startup = [
+      { command = "kitty -T kittyterm";}
+      { command = "sleep 60; kitty -T spotifytui -o confirm_os_window_close=0 spotify_player";}
+    ];
+    window = {
+      border = 1;
+      titlebar = false;
+    };
+    assigns = {
+      "1:一" = [{ app_id = "firefox"; }];
+    };
+    colors = {
+      focused = {
+        # background = "#080808";
+        # border = "#80a0ff";
+        # childBorder = "#80a0ff";
+        # indicator = "#080808";
+        # text = "#ffd700";
+      };
+      unfocused = {
+        # background = "#080808";
+        # border = "#80a0ff";
+        # childBorder = "#303030";
+        # indicator = "#80a0ff";
+        # text = "#c6c6c6";
+      };
+    };
+    floating = {
+      border = 1;
+      criteria = [
+        {title = "^Picture-in-Picture$";}
+        {app_id = "qalculate-gtk";}
+        {app_id = "org.gnome.clocks";}
+        {app_id = "com.github.stsdc.monitor";}
+        {app_id = "blueman";}
+        {app_id = "pavucontrol";}
+        {app_id = "syncthingtray";}
+        {title = "Syncthing Tray";}
+        {app_id = "SchildiChat";}
+        {app_id = "Element";}
+        {app_id = "com.nextcloud.desktopclient.nextcloud";}
+        {app_id = "gnome-system-monitor";}
+        {title = "(?:Open|Save) (?:File|Folder|As)";}
+        {title = "^Add$";}
+        {title = "com-jgoodies-jdiskreport-JDiskReport";}
+        {app_id = "discord";}
+        {window_role = "pop-up";}
+        {window_role = "bubble";}
+        {window_role = "dialog";}
+        {window_role = "task_dialog";}
+        {window_role = "menu";}
+        {window_role = "Preferences";}
+      ];
+      titlebar = false;
+    };
+    window = {
+      commands = [
+        {
+          command = "opacity 0.95";
+          criteria = {
+            class = ".*";
+          };
+        }
+        {
+          command = "opacity 1";
+          criteria = {
+            app_id = "Gimp-2.10";
+          };
+        }
+        {
+          command = "opacity 0.99";
+          criteria = {
+            app_id = "firefox";
+          };
+        }
+        {
+          command = "sticky enable, shadows enable";
+          criteria = {
+            title="^Picture-in-Picture$";
+          };
+        }
+        {
+          command = "opacity 0.8, sticky enable, border normal, move container to scratchpad";
+          criteria = {
+            title="^kittyterm$";
+          };
+        }
+        {
+          command = "opacity 0.95, sticky enable, border normal, move container to scratchpad";
+          criteria = {
+            title="^spotifytui$";
+          };
+        }
+        # {
+        #   command = "resize set width 60 ppt height 60 ppt, sticky enable, move container to scratchpad";
+        #   criteria = {
+        #     app_id="^$";
+        #     class="^$";
+          # };
+        # }
+        {
+
+          command = "resize set width 60 ppt height 60 ppt, sticky enable, move container to scratchpad";
+          criteria = {
+            class="Spotify";
+          };
+        }
+        {
+          command = "sticky enable";
+          criteria = {
+            app_id = "discord";
+          };
+        }
+        {
+          command = "resize set width 60 ppt height 60 ppt, sticky enable";
+          criteria = {
+            class = "Element";
+          };
+        }
+        {
+          command = "resize set width 60 ppt height 60 ppt, sticky enable";
+          criteria = {
+            app_id = "SchildiChat";
+          };
+        }
+      ];
+    };
+    gaps = {
+      inner = 5;
+    };
+  };
+  extraSessionCommands =''
+      export SDL_VIDEODRIVER=wayland
+      export QT_QPA_PLATFORM=wayland
+      export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
+      export _JAVA_AWT_WM_NONREPARENTING=1
+      export XDG_CURRENT_DESKTOP=sway
+      export XDG_SESSION_DESKTOP=sway
+      export QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox";
+      export ANKI_WAYLAND=1;
+      export OBSIDIAN_USE_WAYLAND=1;
+    '';
+  # extraConfigEarly = "
+  # exec systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK
+  # exec hash dbus-update-activation-environment 2>/dev/null && dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK
+  # ";
+  extraConfig =let
+    modifier = config.wayland.windowManager.sway.config.modifier;
+    swayfxSettings = "
+        blur enable
+        blur_xray disable
+        blur_passes 1
+        blur_radius 1
+        shadows enable
+        corner_radius 2
+        titlebar_separator disable
+        default_dim_inactive 0.02
+    ";
+    swayfxSettingsOff = "";
+  in "
+      exec_always autotiling
+      set $exit \"exit: [s]leep, [p]oweroff, [r]eboot, [l]ogout\"
+      mode $exit {
+
+          bindsym --to-code {
+              s exec \"systemctl suspend\", mode \"default\"
+              p exec \"systemctl poweroff\"
+              r exec \"systemctl reboot\"
+              l exec \"swaymsg exit\"
+
+              Return mode \"default\"
+              Escape mode \"default\"
+              ${modifier}+x mode \"default\"
+          }
+      }
+
+      exec systemctl --user import-environment
+
+      ${swayfxSettings}
+
+      ";
+};
+
+
+
+
+
+
+ +
+

3.4. flake.nix template and Closing Parenthesis (this needs to be the last heading in the Systems header)

+
+

+This sections puts together the flake.nix file from the Noweb-Ref blocks section. +

+ +

+Here we also close the opening parenthesis of modules/common.nix (home-manager) and profiles/common.nix (NixOS): +

+
+ +
+

3.4.1. Closing parentheses for common/home.nix and common/nixos.nix

+
+
+
+}
+
+
+
+ +
+
+}
+
+
+
+
+
+ +
+

3.4.2. flake.nix

+
+

+This tangles the flake.nix file; This block only needs to be touched when updating the general structure of the flake. For everything else, see the respective noweb-ref block. +

+ +
+
+  {
+  description = "SwarseFlake - Nix Flake for all SwarselSystems";
+
+  inputs = {
+
+    nixpkgs.url = github:nixos/nixpkgs/nixos-unstable;
+
+
+    # user-level configuration
+    home-manager = {
+      url = github:nix-community/home-manager;
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+
+    # overlay to access bleeding edge emacs
+    emacs-overlay = {
+      url = github:nix-community/emacs-overlay;
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+
+    # nix user repository
+    # i use this mainly to not have to build all firefox extensions
+    # myself as well as for the emacs-init package (tbd)
+    nur.url = github:nix-community/NUR;
+
+    # provides GL to non-NixOS hosts
+    nixgl.url = github:guibou/nixGL;
+
+    # manages all theming using Home-Manager
+    stylix.url = github:danth/stylix;
+
+    # nix secrets management
+    sops-nix.url = github:Mic92/sops-nix;
+
+    # enable secure boot on NixOS
+    lanzaboote.url = github:nix-community/lanzaboote;
+
+    # nix for android
+    nix-on-droid = {
+      url = github:t184256/nix-on-droid/release-23.05;
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+
+    # generate NixOS images
+    nixos-generators = {
+      url = github:nix-community/nixos-generators;
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+
+    # provides expressions for mautrix-signal
+    nixpkgs-mautrix-signal ={
+      url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal;
+    };
+
+    # patches for gaming on nix
+    nix-gaming = {
+      url = github:fufexan/nix-gaming;
+    };
+
+    # hardware quirks on nix
+    nixos-hardware = {
+      url = github:NixOS/nixos-hardware/master;
+    };
+
+    # dynamic library loading
+    nix-alien = {
+      url = github:thiagokokada/nix-alien;
+    };
+
+  };
+
+  outputs = inputs@{
+    self,
+
+      nixpkgs,
+      home-manager,
+      nix-on-droid,
+      nixos-generators,
+      emacs-overlay,
+      nur,
+      nixgl,
+      stylix,
+      sops-nix,
+      lanzaboote,
+      nixpkgs-mautrix-signal,
+      nix-gaming,
+      nixos-hardware,
+      nix-alien,
+
+      ...
+  }: let
+
+    system = "x86_64-linux"; # not very portable, but I do not use other architectures at the moment
+    pkgs = import nixpkgs { inherit system;
+                            overlays = [ emacs-overlay.overlay
+                                         nur.overlay
+                                         nixgl.overlay
+                                       ];
+                            config.allowUnfree = true;
+                          };
+
+    # for ovm arm hosts
+    armpkgs = import nixpkgs { system = "aarch64-linux";
+                            overlays = [ emacs-overlay.overlay
+                                         nur.overlay
+                                         nixgl.overlay
+                                       ];
+                            config.allowUnfree = true;
+                          };
+
+    pkgsmautrix = import nixpkgs-mautrix-signal { inherit system;
+                            config.allowUnfree = true;
+                          };
+
+    # NixOS modules that can only be used on NixOS systems
+    nixModules = [ stylix.nixosModules.stylix
+                   ./profiles/common/nixos.nix
+                   # dynamic library loading
+                   ({ self, system, ... }: {
+                     environment.systemPackages = with self.inputs.nix-alien.packages.${system}; [
+                       nix-alien
+                     ];
+                     # needed for `nix-alien-ld`
+                     programs.nix-ld.enable = true;
+                     })
+                   ];
+
+    # Home-Manager modules wanted on non-NixOS systems
+    homeModules = [ stylix.homeManagerModules.stylix
+                  ];
+    # Home-Manager modules wanted on both NixOS and non-NixOS systems
+    mixedModules = [ sops-nix.homeManagerModules.sops
+                     ./profiles/common/home.nix
+                   ];
+
+  in {
+
+    # NixOS setups - run home-manager as a NixOS module for better compatibility
+    # another benefit - full rebuild on nixos-rebuild switch
+    # run rebuild using `nswitch`
+
+    # NEW HOSTS: For a new host, decide whether a NixOS (nixosConfigurations) or non-NixOS (homeConfigurations) is used.
+    # Make sure to move hardware-configuration to the appropriate location, by default it is found in /etc/nixos/.
+
+    nixosConfigurations = {
+
+      onett = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = nixModules ++ [
+          ./profiles/onett/nixos.nix
+          home-manager.nixosModules.home-manager
+          {
+            home-manager.users.swarsel.imports = mixedModules ++ [
+              ./profiles/onett/home.nix
+            ];
+          }
+        ];
+      };
+
+      sandbox = nixpkgs.lib.nixosSystem {
+        pkgs = pkgsmautrix;
+        specialArgs.unstable = nixpkgs-mautrix-signal;
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/sandbox/nixos.nix
+        ];
+      };
+
+      twoson = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = nixModules ++ [
+          ./profiles/twoson/nixos.nix
+          home-manager.nixosModules.home-manager
+          {
+            home-manager.users.swarsel.imports = mixedModules ++ [
+              ./profiles/twoson/home.nix
+            ];
+          }
+        ];
+      };
+
+      threed = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = nixModules ++ [
+          lanzaboote.nixosModules.lanzaboote
+          ./profiles/threed/nixos.nix
+          home-manager.nixosModules.home-manager
+          {
+            home-manager.users.swarsel.imports = mixedModules ++ [
+              ./profiles/threed/home.nix
+            ];
+          }
+        ];
+      };
+
+      fourside = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = nixModules ++ [
+          nixos-hardware.nixosModules.lenovo-thinkpad-p14s-amd-gen2
+          ./profiles/fourside/nixos.nix
+          home-manager.nixosModules.home-manager
+          {
+            home-manager.users.swarsel.imports = mixedModules ++ [
+              ./profiles/fourside/home.nix
+            ];
+          }
+        ];
+      };
+
+      stand = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = nixModules ++ [
+          ./profiles/stand/nixos.nix
+          home-manager.nixosModules.home-manager
+          {
+            home-manager.users.homelen.imports = mixedModules ++ [
+              ./profiles/stand/home.nix
+            ];
+          }
+        ];
+      };
+
+      nginx = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/nginx/nixos.nix
+        ];
+      };
+
+      calibre = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/calibre/nixos.nix
+        ];
+      };
+
+      jellyfin = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          # sops-nix.nixosModules.sops
+          ./profiles/server1/jellyfin/nixos.nix
+        ];
+      };
+
+      transmission = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/transmission/nixos.nix
+        ];
+      };
+
+      matrix = nixpkgs.lib.nixosSystem {
+        # specialArgs = {inherit pkgsmautrix; };
+        pkgs = pkgsmautrix;
+        # this is to import a service module that is not on nixpkgs
+        # this way avoids infinite recursion errors
+        specialArgs.unstable = nixpkgs-mautrix-signal;
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/matrix/nixos.nix
+        ];
+      };
+
+      sound = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/sound/nixos.nix
+        ];
+      };
+
+      spotifyd = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/spotifyd/nixos.nix
+        ];
+      };
+
+      paperless = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/server1/paperless/nixos.nix
+        ];
+      };
+
+      #ovm swarsel
+      sync = nixpkgs.lib.nixosSystem {
+        specialArgs = {inherit inputs pkgs; };
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/remote/oracle/sync/nixos.nix
+        ];
+      };
+
+      #ovm swarsel
+      swatrix = nixpkgs.lib.nixosSystem {
+        # specialArgs = {inherit pkgsmautrix; };
+        pkgs = pkgsmautrix;
+        # this is to import a service module that is not on nixpkgs
+        # this way avoids infinite recursion errors
+        specialArgs.unstable = nixpkgs-mautrix-signal;
+        modules = [
+          sops-nix.nixosModules.sops
+          ./profiles/remote/oracle/matrix/nixos.nix
+        ];
+      };
+    };
+
+    # pure Home Manager setups - for non-NixOS machines
+    # run rebuild using `hmswitch`
+
+    homeConfigurations = {
+
+      "leons@PCisLee" = home-manager.lib.homeManagerConfiguration {
+        inherit pkgs;
+        modules = homeModules ++ mixedModules ++ [
+          ./profiles/surface/home.nix
+        ];
+      };
+
+    };
+
+    nixOnDroidConfigurations = {
+
+      default = nix-on-droid.lib.nixOnDroidConfiguration {
+        modules = [
+          ./profiles/mysticant/configuration.nix
+        ];
+      };
+
+    };
+
+    packages.x86_64-linux = {
+
+    };
+
+  };
+}
+
+
+
+
+
+
+
+
+

4. Emacs

+
+
+
+

4.1. Initialization (early-init.el)

+
+

+In this section I handle my early init file; it takes care of frame-setup for emacsclient buffers. +

+
+ +
+

4.1.1. Increase startup performance

+
+

+First, I use some advice from doomemacs regarding garbace collection; here I make sure that during startup, the garbace collectur will not run, which will improve startup times. Now, that might not really be needed since I will usually only start the emacs server once during startup and then not touch it again, however, since I am building my emacs configuration using NixOS, there is some merit to this since I will usually need to restart the server once I rebuild my configuration. +

+ +

+Also, inspired by a setting I have seen in protesilaos' configuration, I apply the same idea to the file-name-handler-alist and vc-handled-backends. +

+ +

+In the end, we need to restore those values to values that will work during normal operation. For that, I add a hook to the startup function that will revert the values once Emacs has finished initialization. +

+ +

+Also packed into the hook function is the line (fset 'epg-wait-for-status 'ignore). This line is needed at the end of the configuration in order to allow for my Yubikey to be used to encrypt and decrypt .gpg files. Without it, Emacs will just hang forever and basically crash. +

+ +
+
(defvar swarsel-file-name-handler-alist file-name-handler-alist)
+(defvar swarsel-vc-handled-backends vc-handled-backends)
+
+(setq gc-cons-threshold most-positive-fixnum
+      gc-cons-percentage 0.6
+      file-name-handler-alist nil
+      vc-handled-backends nil)
+
+(add-hook 'emacs-startup-hook
+          (lambda ()
+            (progn
+              (setq gc-cons-threshold (* 1000 1000 8)
+                    gc-cons-percentage 0.1
+                    file-name-handler-alist swarsel-file-name-handler-alist
+                    vc-handled-backends swarsel-vc-handled-backends)
+              (fset 'epg-wait-for-status 'ignore)
+              )))
+
+
+
+
+
+
+

4.1.2. Setup frames

+
+

+Next, I will setup the basic frame for my emacs buffers. Note that I use a tiling window manager, so I do not need to hold myself up with sizing the windows myself. I also disable some GUI tools that I (like many others) do not find to be particularly useful. Also I inhibit many startup functions here, even though it does not affect me greatly since I use another solution for that. +

+ +

+We also make require immediate compilation of native code. +

+ +

+For the default-frame-alist, I used to also set '(right-divider-width . 4) and '(bottom-divider-width . 4), but I did not like the look of the divider bar and usually know my splits anyways, so this is no longer set. +

+ +
+
(tool-bar-mode 0)
+(menu-bar-mode 0)
+(scroll-bar-mode 0)
+
+(setq frame-inhibit-implied-resize t
+      ring-bell-function 'ignore
+      use-dialog-box nil
+      use-file-dialog nil
+      use-short-answers t
+      inhibit-startup-message t
+      inhibit-splash-screen t
+      inhibit-startup-screen t
+      inhibit-x-resources t
+      inhibit-startup-buffer-menu t
+      inhibit-startup-echo-area-message user-login-name ; this needs to be set to the username or it will not have an effect
+      comp-deferred-compilation nil ; compile all Elisp to native code immediately
+      )
+
+(setq-default left-margin-width 1
+              right-margin-width 1)
+
+(setq-default default-frame-alist
+      (append
+       (list
+        '(undecorated . t) ; no title bar, borders etc.
+        '(background-color . "#1D252C") ; load doom-citylight colors to avoid white flash
+        '(foreground-color . "#A0B3C5") ; load doom-citylight colors to avoid white flash
+        '(vertical-scroll-bars . nil)
+        '(horizontal-scroll-bars . nil)
+        '(internal-border-width . 5)
+        '(tool-bar-lines . 0)
+        '(menu-bar-lines . 0))))
+
+
+
+
+
+
+

4.1.3. Make C-i, C-m, C-[ available in graphic sessions

+
+

+By default, emacs binds +

+
    +
  • C-i to the TAB key
  • +
  • C-m to the RET key
  • +
  • C-[ to the ECS key
  • +
+ +

+These keybinds exist to make Emacs work well in terminal mode. However, most of the time I am using Emacs in a graphic session, and I would hence like to have these keybinds available for personal use. +

+ +

+NOTE: To use these keybinds, you need to enclose the binding in angled brackets (<>). Then they can be used normally +

+ +
+
+(add-hook
+    'after-make-frame-functions
+    (lambda (frame)
+      (with-selected-frame frame
+        (when (display-graphic-p)
+          (define-key input-decode-map (kbd "C-i") [DUMMY-i])
+          (define-key input-decode-map (kbd "C-[") [DUMMY-lsb])
+          (define-key input-decode-map (kbd "C-m") [DUMMY-m])
+          ))))
+
+
+
+
+
+
+
+
+
+
+

4.2. Personal settings

+
+

+This section is used to define my own functions, own variables, and own keybindings. +

+
+ +
+

4.2.1. Custom functions

+
+

+In this section I define extra functions that I need. Some of these functions I wrote myself, some I found after internet reseach. For functions I found on the internet, I will link the original source I found it in. +

+
+ +
    +
  1. Emacs/Evil state toggle
    +
    +

    +Since I am rebinding the C-z hotkey for emacs-evil-state toggling, I want to have a function that still lets me perform this action quickly. +

    + +
    +
    +(defun swarsel/toggle-evil-state ()
    +  (interactive)
    +  (if (or (evil-emacs-state-p) (evil-insert-state-p))
    +      (evil-normal-state)
    +    (evil-emacs-state)))
    +
    +
    +
    +
    +
  2. + +
  3. Switching to last used buffer
    +
    +

    +I often find myself bouncing between two buffers when I do not want to use a window split. This funnction simply jumps to the last used buffer. +

    + +
    +
    +(defun swarsel/last-buffer () (interactive) (switch-to-buffer nil))
    +
    +
    +
    +
    +
  4. +
  5. mu4e functions
    +
    +

    +I use these functions to let me switch between my main email accounts, as mu4e by itself has trouble doing so. mu4e-switch-account allows for manual choosing of the sender account, while mu4e-rfs--matching-address and mu4e-send-from-correct-address are used when replying to a mail; they switch the sender account to the one that received the mail. +

    + +

    +By default, the sender email will not be changed after sending a mail; however, I want Emacs to always use my main address when not replying to another email. For that I use mu4e-restore-default. +

    + +

    +Used here: mu4e +

    + +
    +
    +(defun swarsel/mu4e-switch-account ()
    +  (interactive)
    +  (let ((account (completing-read "Select account: " mu4e-user-mail-address-list)))
    +    (setq user-mail-address account)))
    +
    +(defun swarsel/mu4e-rfs--matching-address ()
    +  (cl-loop for to-data in (mu4e-message-field mu4e-compose-parent-message :to)
    +           for to-email = (pcase to-data
    +                            (`(_ . email) email)
    +                            (x (mu4e-contact-email x)))
    +           for to-name =  (pcase to-data
    +                            (`(_ . name) name)
    +                            (x (mu4e-contact-name x)))
    +           when (mu4e-user-mail-address-p to-email)
    +           return (list to-name to-email)))
    +
    +(defun swarsel/mu4e-send-from-correct-address ()
    +  (when mu4e-compose-parent-message
    +    (save-excursion
    +      (when-let ((dest (swarsel/mu4e-rfs--matching-address)))
    +        (cl-destructuring-bind (from-user from-addr) dest
    +          (setq user-mail-address from-addr)
    +          (message-position-on-field "From")
    +          (message-beginning-of-line)
    +          (delete-region (point) (line-end-position))
    +          (insert (format "%s <%s>" (or from-user user-full-name) from-addr)))))))
    +
    +(defun swarsel/mu4e-restore-default ()
    +  (setq user-mail-address "leon@swarsel.win"
    +        user-full-name "Leon Schwarzäugl"))
    +
    +
    +
    +
    +
    +
  6. +
  7. Create non-existant directories when finding file
    +
    +

    +This function will check if a directory for which a file we want to open exists; if not, it will offer to create the directories for me. +

    + +
    +
    +(defun swarsel/with-buffer-name-prompt-and-make-subdirs ()
    +  (let ((parent-directory (file-name-directory buffer-file-name)))
    +    (when (and (not (file-exists-p parent-directory))
    +               (y-or-n-p (format "Directory `%s' does not exist! Create it? " parent-directory)))
    +      (make-directory parent-directory t))))
    +
    +(add-to-list 'find-file-not-found-functions #'swarsel/with-buffer-name-prompt-and-make-subdirs)
    +
    +
    +
    +
    +
  8. +
  9. [crux] Duplicate Lines
    +
    +

    +When programming, I like to be able to duplicate a line. There are easier functions than the one below, but they either +

    + +
      +
    1. screw with undo/redo
    2. +
    3. move the cursor wildly
    4. +
    + +

    +The below function avoids these problems. Originally I used the function duplicate-line found here: https://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs +

    + +

    +However, this function does not work on regions. Later, I found a solution implemented by crux. I do not need the whole package, so I just extracted the three functions I needed from it. +

    + +
    +
    +(defun crux-get-positions-of-line-or-region ()
    +  "Return positions (beg . end) of the current line or region."
    +  (let (beg end)
    +    (if (and mark-active (> (point) (mark)))
    +        (exchange-point-and-mark))
    +    (setq beg (line-beginning-position))
    +    (if mark-active
    +        (exchange-point-and-mark))
    +    (setq end (line-end-position))
    +    (cons beg end)))
    +
    +(defun crux-duplicate-current-line-or-region (arg)
    +    "Duplicates the current line or region ARG times.
    +  If there's no region, the current line will be duplicated.  However, if
    +  there's a region, all lines that region covers will be duplicated."
    +    (interactive "p")
    +    (pcase-let* ((origin (point))
    +                 (`(,beg . ,end) (crux-get-positions-of-line-or-region))
    +                 (region (buffer-substring-no-properties beg end)))
    +      (dotimes (_i arg)
    +        (goto-char end)
    +        (newline)
    +        (insert region)
    +        (setq end (point)))
    +      (goto-char (+ origin (* (length region) arg) arg))))
    +
    +(defun crux-duplicate-and-comment-current-line-or-region (arg)
    +  "Duplicates and comments the current line or region ARG times.
    +If there's no region, the current line will be duplicated.  However, if
    +there's a region, all lines that region covers will be duplicated."
    +  (interactive "p")
    +  (pcase-let* ((origin (point))
    +               (`(,beg . ,end) (crux-get-positions-of-line-or-region))
    +               (region (buffer-substring-no-properties beg end)))
    +    (comment-or-uncomment-region beg end)
    +    (setq end (line-end-position))
    +    (dotimes (_ arg)
    +      (goto-char end)
    +      (newline)
    +      (insert region)
    +      (setq end (point)))
    +    (goto-char (+ origin (* (length region) arg) arg))))
    +
    +
    +
    +
    +
  10. +
  11. [prot] org-id-headings
    +
    +

    +These functions by protesilaos generate heading links in an org-file similar to the normal org-store-link approach when not using properties. This approach has a weakness however - if the heading name is changed, the link breaks. These functions generate a unique identifier for each heading which will not break and also works when exporting the file to html, for example. +

    + +
    +
    +(defun prot-org--id-get ()
    +  "Get the CUSTOM_ID of the current entry.
    +If the entry already has a CUSTOM_ID, return it as-is, else
    +create a new one."
    +  (let* ((pos (point))
    +         (id (org-entry-get pos "CUSTOM_ID")))
    +    (if (and id (stringp id) (string-match-p "\\S-" id))
    +        id
    +      (setq id (org-id-new "h"))
    +      (org-entry-put pos "CUSTOM_ID" id)
    +      id)))
    +
    +(declare-function org-map-entries "org")
    +
    +(defun prot-org-id-headlines ()
    +  "Add missing CUSTOM_ID to all headlines in current file."
    +  (interactive)
    +  (org-map-entries
    +   (lambda () (prot-org--id-get))))
    +
    +(defun prot-org-id-headline ()
    +  "Add missing CUSTOM_ID to headline at point."
    +  (interactive)
    +  (prot-org--id-get))
    +
    +
    +
    +
    +
  12. +
  13. Inhibit Messages in Echo Area
    +
    +

    +Emacs likes to send messages to the echo area; this is generally a good thing. However, it bothers me a lot when I am currently working in minibuffer where I receive an echo area message that is actually important and it is then overwritten by e.g. the mu4e update message. This section makes it possible to find the root function calling the message function and disabling it here. +

    + +

    +Usage: Enable the (advice-add 'message :around #'who-called-me?) by running this code block, which will show a full trace of all messages being sent to the echo area: +

    + +
    +
    +(advice-add 'message :around #'who-called-me?)
    +
    +
    +
    + +

    +Once the root function has been found, it can be disabled via advice=add as in the last block in this section. To disable the stack tracing, run (advice-remove 'message #'who-called-me?) or the following code block: +

    + +
    +
    +(advice-remove 'message #'who-called-me?)
    +
    +
    +
    + +

    +Lastly, individual messages can be reenabled using the (advice-remove '<FUNCTION-NAME> #'suppress-messages) approach. Use this when you accidentally disabled a helpful message. +

    + + +
    +
    +(defun suppress-messages (old-fun &rest args)
    +  (cl-flet ((silence (&rest args1) (ignore)))
    +    (advice-add 'message :around #'silence)
    +    (unwind-protect
    +        (apply old-fun args)
    +      (advice-remove 'message #'silence))))
    +
    +(advice-add 'pixel-scroll-precision :around #'suppress-messages)
    +(advice-add 'mu4e--server-filter :around #'suppress-messages)
    +(advice-add 'org-unlogged-message :around #'suppress-messages)
    +(advice-add 'magit-auto-revert-mode--init-kludge  :around #'suppress-messages)
    +(advice-add 'push-mark  :around #'suppress-messages)
    +
    +;; to reenable
    +;; (advice-remove 'timer-event-handler #'suppress-messages)
    +
    +(defun who-called-me? (old-fun format &rest args)
    +  (let ((trace nil) (n 1) (frame nil))
    +    (while (setf frame (backtrace-frame n))
    +      (setf n     (1+ n)
    +            trace (cons (cadr frame) trace)) )
    +    (apply old-fun (concat "<<%S>>\n" format) (cons trace args))))
    +
    +;; enable to get message backtrace, the first function shown in backtrace calls the other functions
    +;; (advice-add 'message :around #'who-called-me?)
    +
    +;; disable to stop receiving backtrace
    +(advice-remove 'message #'who-called-me?)
    +
    +
    +
    +
    +
    +
  14. +
  15. Move up one directory for find-file
    +
    +

    +I find it very annoying that the standard behavior for M-DEL only deletes one word when using find-file. This function makes it so that we always go up by one directory level instead. +

    + +

    +This function was found here: https://www.reddit.com/r/emacs/comments/re31i6/how_to_go_up_one_directory_when_using_findfile_cx/ +

    + +
    +
    +(defun up-directory (path)
    +  "Move up a directory in PATH without affecting the kill buffer."
    +  (interactive "p")
    +  (if (string-match-p "/." (minibuffer-contents))
    +      (let ((end (point)))
    +        (re-search-backward "/.")
    +        (forward-char)
    +        (delete-region (point) end))))
    +
    +(define-key minibuffer-local-filename-completion-map
    +            [C-backspace] #'up-directory)
    +
    +
    +
    +
    +
  16. +
  17. org-mode: General setup
    +
    +

    +Sets up the basic settings that I want to have active in org-mode buffers. +

    + +

    +Used here: General org-mode +

    + +
    +
    +(defun swarsel/org-mode-setup ()
    +  (org-indent-mode)
    +  (variable-pitch-mode 1)
    +  ;;(auto-fill-mode 0)
    +  (setq display-line-numbers-type 'relative
    +        display-line-numbers-current-absolute 1
    +        display-line-numbers-width-start nil
    +        display-line-numbers-width 6
    +        display-line-numbers-grow-only 1)
    +  (add-hook 'org-tab-first-hook 'org-end-of-line)
    +  (visual-line-mode 1))
    +
    +
    +
    +
    +
  18. +
  19. org-mode: Visual-fill column
    +
    +

    +This function sets the width of buffers in org-mode. +

    + +

    +Used in: Centered org-mode Buffers +

    + +
    +
    +(defun swarsel/org-mode-visual-fill ()
    +  (setq visual-fill-column-width 150
    +        visual-fill-column-center-text t)
    +  (visual-fill-column-mode 1))
    +
    +
    +
    +
    +
  20. + +
  21. org-mode: Auto-tangle and export Configuration Files
    +
    +

    +This section automatically tangles all configuration blocks in this file to the defined Emacs org-file. It also exports the configuration file as html. +

    + + + +
    +
    +(defun swarsel/org-babel-tangle-config ()
    +  (when (string-equal (buffer-file-name)
    +                      swarsel-swarsel-org-filepath)
    +    ;; Dynamic scoping to the rescue
    +    (let ((org-confirm-babel-evaluate nil))
    +      (org-html-export-to-html)
    +      (org-babel-tangle)))
    +  (when (string-equal (buffer-file-name)
    +                      swarsel-emacs-org-filepath)
    +    ;; Dynamic scoping to the rescue
    +    (let ((org-confirm-babel-evaluate nil))
    +      (org-html-export-to-html)
    +      (org-babel-tangle)))
    +  (when (string-equal (buffer-file-name)
    +                      swarsel-nix-org-filepath)
    +    ;; Dynamic scoping to the rescue
    +    (let ((org-confirm-babel-evaluate nil))
    +      (org-babel-tangle))))
    +
    +(setq org-html-htmlize-output-type nil)
    +
    +(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config)))
    +
    +
    +
    +
    +
    +
    +
  22. + +
  23. org-mode: Fold current heading
    +
    +

    +Normally emacs cycles between three states: +

    + +
      +
    1. fully folded
    2. +
    3. One heading expanded
    4. +
    5. All headings expanded
    6. +
    + +

    +However, I want to be able to fold a single heading consistently. +

    + +
    +
    +(defun org-fold-outer ()
    +  (interactive)
    +  (org-beginning-of-line)
    +  (if (string-match "^*+" (thing-at-point 'line t))
    +      (outline-up-heading 1))
    +  (outline-hide-subtree)
    +  )
    +
    +
    +
    +
    +
  24. + +
  25. corfu: Do not interrupt navigation
    +
    +

    +These three functions allow me to keep using the normal navigation keys even when a corfu completion pops up. +

    + +

    +These functions are used here: Corfu +

    + +
    +
    +(defun swarsel/corfu-normal-return (&optional arg)
    +  (interactive)
    +  (corfu-quit)
    +  (newline)
    +  )
    +
    +(defun swarsel/corfu-quit-and-up (&optional arg)
    +  (interactive)
    +  (corfu-quit)
    +  (evil-previous-visual-line))
    +
    +(defun swarsel/corfu-quit-and-down (&optional arg)
    +  (interactive)
    +  (corfu-quit)
    +  (evil-next-visual-line))
    +
    +
    +
    +
    +
  26. + +
  27. python shell reloading
    +
    +

    +The standard Emacs behaviour for the Python process shell is a bit annoying. This is my attempt at making it show automatically on opening a python buffer and making it refresh on its own as well. This does not nicely work yet. +

    + +
    +
    +    ;; run the python inferior shell immediately upon entering a python buffer
    +    ;; (add-hook 'python-mode-hook 'swarsel/run-python)
    +
    +  ;; (defun swarsel/run-python ()
    +  ;;   (save-selected-window
    +  ;;     (switch-to-buffer-other-window (process-buffer (python-shell-get-or-create-process (python-shell-parse-command))))))
    +
    +;; reload python shell automatically
    +(defun my-python-shell-run ()
    +  (interactive)
    +  (when (get-buffer-process "*Python*")
    +     (set-process-query-on-exit-flag (get-buffer-process "*Python*") nil)
    +     (kill-process (get-buffer-process "*Python*"))
    +     ;; Uncomment If you want to clean the buffer too.
    +     ;;(kill-buffer "*Python*")
    +     ;; Not so fast!
    +     (sleep-for 0.5))
    +  (run-python (python-shell-parse-command) nil nil)
    +  (python-shell-send-buffer)
    +  ;; Pop new window only if shell isnt visible
    +  ;; in any frame.
    +  (unless (get-buffer-window "*Python*" t)
    +    (python-shell-switch-to-shell)))
    +
    +(defun my-python-shell-run-region ()
    +  (interactive)
    +  (python-shell-send-region (region-beginning) (region-end))
    +  (python-shell-switch-to-shell))
    +
    +
    +
    +
    +
  28. +
+
+ +
+

4.2.2. Custom Keybindings

+
+

+This defines a set of keybinds that I want to have available globally. I have one set of keys that is globally available through the C-SPC prefix. This set is used mostly for functions that I have trouble remembering the original keybind for, or that I just want to have gathered in a common space. +

+ +

+I also define some keybinds to some combinations directly. Those are used mostly for custom functions that I call often enough to warrant this. +

+ +
+
+;; Make ESC quit prompts
+(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
+
+;; Set up general keybindings
+(use-package general
+  :config
+  (general-create-definer swarsel/leader-keys
+    :keymaps '(normal insert visual emacs)
+    :prefix "SPC"
+    :global-prefix "C-SPC")
+
+  (swarsel/leader-keys
+    "e"  '(:ignore e :which-key "evil")
+    "eo" '(evil-jump-backward :which-key "cursor jump backwards")
+    "eO" '(evil-jump-forward :which-key "cursor jump forwards")
+    "t"  '(:ignore t :which-key "toggles")
+    "ts" '(hydra-text-scale/body :which-key "scale text")
+    "te" '(swarsel/toggle-evil-state :which-key "emacs/evil")
+    "tl" '(display-line-numbers-mode :which-key "line numbers")
+    "tp" '(evil-cleverparens-mode :wk "cleverparens")
+    "to" '(olivetti-mode :wk "olivetti")
+    "td" '(darkroom-tentative-mode :wk "darkroom")
+    "tw" '((lambda () (interactive) (toggle-truncate-lines)) :which-key "line wrapping")
+    "m"  '(:ignore m :which-key "modes/programs")
+    "mm" '((lambda () (interactive) (mu4e)) :which-key "mu4e")
+    "mg" '((lambda () (interactive) (magit-list-repositories)) :which-key "magit-list-repos")
+    "mc" '((lambda () (interactive) (swarsel/open-calendar)) :which-key "calendar")
+    "mp" '(popper-toggle :which-key "popper")
+    "md" '(dirvish :which-key "dirvish")
+    "o"  '(:ignore o :which-key "org")
+    "op" '((lambda () (interactive) (org-present)) :which-key "org-present")
+    "ob" '((lambda () (interactive) (org-babel-mark-block)) :which-key "Mark whole src-block")
+    "ol" '((lambda () (interactive) (org-insert-link)) :which-key "insert link")
+    "os" '((lambda () (interactive) (org-store-link)) :which-key "store link")
+    "od" '((lambda () (interactive) (org-babel-demarcate-block)) :which-key "demarcate (split) src-block")
+    ;; "c"  '(:ignore c :which-key "capture")
+    ;; "cj" '((lambda () (interactive) (org-capture nil "jj")) :which-key "journal")
+    ;; "cs" '(markdown-download-screenshot :which-key "screenshot")
+    "l"  '(:ignore l :which-key "links")
+    "lc" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (org-overview) )) :which-key "SwarselSystems.org")
+    "le" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "Emacs") ) (org-overview) (org-cycle) )) :which-key "Emacs.org")
+    "ln" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "System") ) (org-overview) (org-cycle))) :which-key "Nixos.org")
+    "ls" '((lambda () (interactive) (find-file "/smb:Swarsel@192.168.1.3:")) :which-key "Server")
+    "lo" '(dired swarsel-obsidian-vault-directory :which-key "obsidian")
+    ;; "la" '((lambda () (interactive) (find-file swarsel-org-anki-filepath)) :which-key "anki")
+    ;; "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org")
+    "lp" '((lambda () (interactive) (projectile-switch-project)) :which-key "switch project")
+    "lg" '((lambda () (interactive) (magit-list-repositories)) :which-key "list git repos")
+    ;; "a"   '(:ignore a :which-key "anki")
+    ;; "ap"  '(anki-editor-push-tree :which-key "push new cards")
+    ;; "an"  '((lambda () (interactive) (org-capture nil "a")) :which-key "new card")
+    ;; "as"  '(swarsel-anki-set-deck-and-notetype :which-key "change deck and notetype")
+    "h"   '(:ignore h :which-key "help")
+    "hy"  '(yas-describe-tables :which-key "yas tables")
+    "hb"  '(embark-bindings :which-key "current key bindings")
+    "h"   '(:ignore t :which-key "describe")
+    "he"  'view-echo-area-messages
+    "hf"  'describe-function
+    "hF"  'describe-face
+    "hl"  '(view-lossage :which-key "show command keypresses")
+    "hL"  'find-library
+    "hm"  'describe-mode
+    "ho"  'describe-symbol
+    "hk"  'describe-key
+    "hK"  'describe-keymap
+    "hp"  'describe-package
+    "hv"  'describe-variable
+    "hd"  'devdocs-lookup
+    "w"   '(:ignore t :which-key "window")
+    "wl"  'windmove-right
+    "wh"  'windmove-left
+    "wk"  'windmove-up
+    "wj"  'windmove-down
+    "wr"  'winner-redo
+    "wd"  'delete-window
+    "w="  'balance-windows-area
+    "wD"  'kill-buffer-and-window
+    "wu"  'winner-undo
+    "wr"  'winner-redo
+    "w/"  'evil-window-vsplit
+    "w-"  'evil-window-split
+    "wm"  '(delete-other-windows :wk "maximize")
+    ))
+
+;; General often used hotkeys
+(general-define-key
+ "C-M-a" (lambda () (interactive) (org-capture nil "a")) ; make new anki card
+ ;; "C-M-d" 'swarsel-obsidian-daily ; open daily obsidian file and create if not exist
+ ;; "C-M-S" 'swarsel-anki-set-deck-and-notetype ; switch deck and notetype for new anki cards
+ ;; "C-M-s" 'markdown-download-screenshot ; wrapper for org-download-screenshot
+ "C-c d" 'crux-duplicate-current-line-or-region
+ "C-c D" 'crux-duplicate-and-comment-current-line-or-region
+ "<DUMMY-m>" 'swarsel/last-buffer
+ "M-\\" 'indent-region
+ "C-<f9>" 'my-python-shell-run
+ )
+
+
+
+
+
+
+

4.2.3. Directory setup / File structure

+
+

+In this section I setup some aliases that I use for various directories on my system. Some of these are actually used for magit repository finding etc., but many of them serve no real use and I need to clean this up someday. +

+ +
+
+;; set Nextcloud directory for journals etc.
+(setq swarsel-sync-directory "~/Nextcloud"
+      swarsel-emacs-directory "~/.emacs.d"
+      swarsel-dotfiles-directory "~/.dotfiles"
+      swarsel-projects-directory "~/Documents/GitHub")
+
+(setq swarsel-emacs-org-filepath (expand-file-name "Emacs.org" swarsel-dotfiles-directory)
+      swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory)
+      swarsel-swarsel-org-filepath (expand-file-name "SwarselSystems.org" swarsel-dotfiles-directory)
+      )
+
+
+;; set Emacs main configuration .org names
+(setq swarsel-emacs-org-file "Emacs.org"
+      swarsel-anki-org-file "Anki.org"
+      swarsel-tasks-org-file "Tasks.org"
+      swarsel-archive-org-file "Archive.org"
+      swarsel-org-folder-name "Org"
+      swarsel-obsidian-daily-folder-name "⭐ Personal/Journal"
+      swarsel-obsidian-folder-name "Obsidian"
+      swarsel-obsidian-vault-name "Main")
+
+
+;; set directory paths
+(setq swarsel-org-directory (expand-file-name swarsel-org-folder-name  swarsel-sync-directory)) ; path to org folder
+(setq swarsel-obsidian-directory (expand-file-name swarsel-obsidian-folder-name swarsel-sync-directory)) ; path to obsidian
+(setq swarsel-obsidian-vault-directory (expand-file-name swarsel-obsidian-vault-name swarsel-obsidian-directory)) ; path to obsidian vault
+(setq swarsel-obsidian-daily-directory (expand-file-name swarsel-obsidian-daily-folder-name swarsel-obsidian-vault-directory)) ; path to obsidian daily folder
+
+;; filepaths to certain documents
+(setq swarsel-org-anki-filepath (expand-file-name swarsel-anki-org-file swarsel-org-directory) ; path to anki export file
+      swarsel-org-tasks-filepath (expand-file-name swarsel-tasks-org-file swarsel-org-directory)
+      swarsel-org-archive-filepath (expand-file-name swarsel-archive-org-file swarsel-org-directory))
+
+
+
+
+
+
+
+ +
+

4.2.4. Unclutter .emacs.d

+
+

+In this section I move the custom.el out of it's standard location in .emacs.d. Firstly, I dislike using this file at all since I would rather have fully stateful configuration as commanded by this file. Secondly, this file is too easily permanently changed. Recently I figured out the last bits that I needed to remove from custom.el to no longer be reliant on it, so I now just write it to a temporary file (through make-temp=file) which will be cleaned on shutdown. However, I like to retain the custom framework because it is nice for testing out theme customizations, hence why I still load the file. +

+ +

+This section also sets the emacs directory to the ~/.cache/ directory which is useful for files that I do not want to have lying around in my .emacs.d. +

+ +
+
+;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d
+(setq user-emacs-directory (expand-file-name "~/.cache/emacs/")
+      url-history-file (expand-file-name "url/history" user-emacs-directory))
+
+;; Use no-littering to automatically set common paths to the new user-emacs-directory
+(use-package no-littering)
+(setq custom-file (make-temp-file "emacs-custom-"))
+(load custom-file t)
+
+
+
+
+
+ +
+

4.2.5. Move backup files to another location

+
+

+Many people dislike the Emacs backup files; I do enjoy them, but have to admit that they clutter the filesystem a little too much. Also, I rarely need to access these over different sessions. Hence I move them to /tmp - if Emacs unexpectedly crashes, the files can be recovered, but the backup files will not gather everywhere and will be deleted upon shutdown. +

+ +
+
+(let ((backup-dir "~/tmp/emacs/backups")
+      (auto-saves-dir "~/tmp/emacs/auto-saves/"))
+  (dolist (dir (list backup-dir auto-saves-dir))
+    (when (not (file-directory-p dir))
+      (make-directory dir t)))
+  (setq backup-directory-alist `(("." . ,backup-dir))
+        auto-save-file-name-transforms `((".*" ,auto-saves-dir t))
+        auto-save-list-file-prefix (concat auto-saves-dir ".saves-")
+        tramp-backup-directory-alist `((".*" . ,backup-dir))
+        tramp-auto-save-directory auto-saves-dir))
+
+(setq backup-by-copying t    ; Don't delink hardlinks
+      delete-old-versions t  ; Clean up the backups
+      version-control t      ; Use version numbers on backups,
+      kept-new-versions 5    ; keep some new versions
+      kept-old-versions 2)   ; and some old ones, too
+
+
+
+
+
+
+
+

4.3. General init.el setup + UI

+
+

+In this general section I have settings that I either consider to be integral to my experience when using emacs or have no other section that I feel they belong to. +

+
+ +
+

4.3.1. General setup

+
+

+Here I set up some things that are too minor to put under other categories. +

+
    +
  • Firstly we disable to having to type `yes` and `no` and switch it to `y` and `n`.
  • +
  • We also enable the marking of trailing whitespaces.
  • +
  • Also, make emacs highlight the current line globally
  • +
  • Emacs defaults to pausing all display redrawing on any input. This may have been useful previously, but is not necessary nowadays.
  • +
  • I also disable the suspend-frame function, as I never use it and it is quite confusing when accidentally hitting the keys for it.
  • +
+ +
+
+;; use UTF-8 everywhere
+(set-language-environment "UTF-8")
+
+;; set default font size
+(defvar swarsel/default-font-size 130)
+(setq swarsel-standard-font "FiraCode Nerd Font Mono"
+      swarsel-alt-font "FiraCode Nerd Font Mono")
+
+;; (defalias 'yes-or-no-p 'y-or-n-p)
+;;(setq-default show-trailing-whitespace t)
+(add-hook 'before-save-hook 'delete-trailing-whitespace)
+(global-hl-line-mode 1)
+;; (setq redisplay-dont-pause t) ;; obsolete
+(setq blink-cursor-mode nil) ;; blink-cursor is an unexpected source of slowdown
+(global-subword-mode 1) ; Iterate through CamelCase words
+(setq blink-matching-paren nil) ;; this makes the cursor jump around annoyingly
+(delete-selection-mode 1)
+(setq vc-follow-symlinks t)
+(setq require-final-newline t)
+(winner-mode 1)
+(setq load-prefer-newer t)
+
+(setq undo-limit 80000000
+      evil-want-fine-undo t
+      auto-save-default t
+      password-cache-expiry nil
+      )
+(setq browse-url-browser-function 'browse-url-firefox)
+;; disable a keybind that does more harm than good
+(global-set-key [remap suspend-frame]
+                (lambda ()
+                  (interactive)
+                  (message "This keybinding is disabled (was 'suspend-frame')")))
+
+(setq visible-bell nil)
+(setq initial-major-mode 'fundamental-mode
+      initial-scratch-message nil)
+
+(add-hook 'prog-mode-hook 'display-line-numbers-mode)
+(add-hook 'text-mode-hook 'display-line-numbers-mode)
+
+
+
+
+
+ +
+

4.3.2. Mark all themes as safe

+
+

+Normally when switching themes in emacs, the user will be warned that themes can run malicious code. I only run one theme really and deem it safe. It is however annoying to be asked this on every new system and it also creates lines in custom.el to answer that query, so here I declare all themes as safe. +

+ +
+
+(setq custom-safe-themes t)
+
+
+
+
+
+
+

4.3.3. Show less compilation warnings

+
+

+When Emacs compiles stuff, it often shows a bunch of warnings that I do not need to deal with. Here we silence those. Some will be disabled completely, and some only when we have native compilation available (which should be most of the time, however). +

+ +
+
+(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
+;; Make native compilation silent and prune its cache.
+(when (native-comp-available-p)
+  (setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation
+  (setq native-compile-prune-cache t)) ; Emacs 29
+
+
+
+
+
+ +
+

4.3.4. Indentation

+
+

+Here I define several options related to indentation; I first make it so that only whitespace will be used instead of tab characters for indentation, and I also set a small standard indent. +

+ +

+We set tab-always-indent to 'complete in order to indent first and then do completion if there are any. Also we make it so that python will not complain about missing indentation info. +

+ +

+Lastly, I load the highlight-indent-guides package. This adds a neat visual indicator of the indentation level, which is useful for languages like python. +

+ +
+
+(setq-default indent-tabs-mode nil
+              tab-width 2)
+
+(setq tab-always-indent 'complete)
+(setq python-indent-guess-indent-offset-verbose nil)
+
+(use-package highlight-indent-guides
+  :hook (prog-mode . highlight-indent-guides-mode)
+  :init
+  (setq highlight-indent-guides-method 'column)
+  (setq highlight-indent-guides-responsive 'top)
+  )
+
+(with-eval-after-load 'highlight-indent-guides
+  (set-face-attribute 'highlight-indent-guides-even-face nil :background "gray10")
+  (set-face-attribute 'highlight-indent-guides-odd-face nil :background "gray20")
+  (set-face-attribute 'highlight-indent-guides-stack-even-face nil :background "gray40")
+  (set-face-attribute 'highlight-indent-guides-stack-odd-face nil :background "gray50"))
+
+
+
+
+ +
+

4.3.5. Scrolling

+
+

+By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying. This sets up more granular scrolling that allows scrolling with a mouse wheel or the two-finger touchscreen gesture. This now also works in buffers with a very small frame. +

+ +
+
+(setq mouse-wheel-scroll-amount
+      '(1
+        ((shift) . 5)
+        ((meta) . 0.5)
+        ((control) . text-scale))
+      mouse-drag-copy-region nil
+      make-pointer-invisible t
+      mouse-wheel-progressive-speed t
+      mouse-wheel-follow-mouse t)
+
+(setq-default scroll-preserve-screen-position t
+              scroll-conservatively 1
+              scroll-margin 0
+              next-screen-context-lines 0)
+
+(pixel-scroll-precision-mode 1)
+
+
+
+
+
+ +
+

4.3.6. Evil

+
+
+ +
    +
  1. General evil
    +
    +

    +This setups up evil, which brings vim-like keybindings to emacs. In the same location, I also unbind the C-z key (I am very unhappy with this implementation, but it is the only thing that works consistently so far) to make it available for cape later. +

    + +

    +Also, I setup initial modes for several major-modes depending on what I deem fit. +

    + +
    +
    +;; Emulate vim in emacs
    +(use-package evil
    +  :init
    +  (setq evil-want-integration t) ; loads evil
    +  (setq evil-want-keybinding nil) ; loads "helpful bindings" for other modes
    +  (setq evil-want-C-u-scroll t) ; scrolling using C-u
    +  (setq evil-want-C-i-jump nil) ; jumping with C-i
    +  (setq evil-want-Y-yank-to-eol t) ; give Y some utility
    +  (setq evil-shift-width 2) ; uniform indent
    +  (setq evil-respect-visual-line-mode t) ; i am torn on this one
    +  (setq evil-split-window-below t)
    +  (setq evil-vsplit-window-right t)
    +  :config
    +  (evil-mode 1)
    +  (define-key evil-normal-state-map (kbd "C-z") nil)
    +  (define-key evil-insert-state-map (kbd "C-z") nil)
    +  (define-key evil-visual-state-map (kbd "C-z") nil)
    +  (define-key evil-motion-state-map (kbd "C-z") nil)
    +  (define-key evil-operator-state-map (kbd "C-z") nil)
    +  (define-key evil-replace-state-map (kbd "C-z") nil)
    +  (define-key global-map (kbd "C-z") nil)
    +  (evil-set-undo-system 'undo-tree)
    +
    +  ;; Don't use evil-mode in these contexts, or use it in a specific mode
    +  (evil-set-initial-state 'messages-buffer-mode 'emacs)
    +  (evil-set-initial-state 'dashboard-mode 'emacs)
    +  (evil-set-initial-state 'dired-mode 'emacs)
    +  (evil-set-initial-state 'cfw:details-mode 'emacs)
    +  (evil-set-initial-state 'Custom-mode 'emacs) ; god knows why this mode is in uppercase
    +  (evil-set-initial-state 'mu4e-headers-mode 'normal)
    +  (evil-set-initial-state 'python-inferior-mode 'normal)
    +  (add-hook 'org-capture-mode-hook 'evil-insert-state)
    +  (add-to-list 'evil-buffer-regexps '("COMMIT_EDITMSG" . insert)))
    +
    +
    +
    +
    +
  2. +
  3. evil-collection
    +
    +

    +This gives support for many different modes, and works beautifully out of the box. +

    + +
    +
    +(use-package evil-collection
    +  :after evil
    +  :config
    +  (evil-collection-init)
    +  (setq forge-add-default-bindings nil))
    +
    +
    +
    +
  4. +
  5. evil-snipe
    +
    +

    +This package changes the char-search commands like f by showing the results in a more visual manner. It also gives a 2-character search using s and S. +

    + +
    +
    ;; enables 2-char inline search
    +  (use-package evil-snipe
    +    :after evil
    +    :demand
    +    :config
    +    (evil-snipe-mode +1)
    +    ;; replace 1-char searches (f&t) with this better UI
    +    (evil-snipe-override-mode +1))
    +
    +
    +
    +
  6. + +
  7. evil-cleverparens
    +
    +

    +This helps keeping parentheses balanced which is useful when writing in languages like Elisp. I do not activate this by default, as most languages do not profit from this enough in my eyes. +

    + +
    +
    ;; for parentheses-heavy languades modify evil commands to keep balance of parantheses
    +(use-package evil-cleverparens)
    +
    +
    +
    +
    +
  8. + +
  9. evil-surround
    +
    +

    +This minor-mode adds functionality for doing better surround-commands; for example ci[ will let you change the word within square brackets. +

    + +
    +
    +;; enables surrounding text with S
    +(use-package evil-surround
    +  :config
    +  (global-evil-surround-mode 1))
    +
    +
    +
    +
    +
  10. +
+
+ +
+

4.3.7. ispell

+
+

+This should setup a wordlist that can be used as a dictionary. However, for some reason this does not work, and I will need to further investigate this issue. +

+ +
+
+;; set the NixOS wordlist by hand
+(setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST")
+
+
+
+
+
+ +
+

4.3.8. Font Configuration

+
+

+Here I define my fonts to be used. Honestly I do not understand the face-attributes and pitches of emacs all too well. It seems this configuration works fine, but I might have to revisit this at some point in the future. +

+ +
+
+(dolist (face '(default fixed-pitch))
+  (set-face-attribute face nil
+                      :font "FiraCode Nerd Font Mono"))
+(add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono"))
+
+(set-face-attribute 'default nil :height 100)
+(set-face-attribute 'fixed-pitch nil :height 1.0)
+
+(set-face-attribute 'variable-pitch nil
+                    :family "IBM Plex Sans"
+                    :weight 'regular
+                    :height 1.06)
+
+;; these settings used to be in custom.el
+
+
+
+
+
+ +
+

4.3.9. Theme

+
+

+I have grown to love the doom-citylights theme and have modeled my whole system after it. Also solaire-mode is a nice mode that inverts the alt-faces with the normal faces for specific 'minor' buffers (like Help-buffers). +

+ +
+
+(use-package solaire-mode
+  :custom
+  (solaire-global-mode +1))
+
+(use-package doom-themes
+  :hook
+  (server-after-make-frame . (lambda () (load-theme
+                                         'doom-city-lights t)))
+  :config
+  (load-theme 'doom-city-lights t)
+  (doom-themes-treemacs-config)
+  (doom-themes-org-config))
+
+
+
+
+
+ +
+

4.3.10. Icons

+
+

+This section loads the base icons used in my configuration. I am using nerd-icons over all-the-icons since the former seems to have more integrations with different packages than the latter. +

+ +

+Used in: +

+ + +
+
+(use-package nerd-icons)
+
+
+
+
+
+ +
+

4.3.11. Variable Pitch Mode

+
+

+This minor mode allows mixing fixed and variable pitch fonts within the same buffer. +

+ +
+
+(use-package mixed-pitch
+  :custom
+  (mixed-pitch-set-height nil)
+  (mixed-pitch-variable-pitch-cursor nil)
+  :hook
+  (text-mode . mixed-pitch-mode))
+
+
+
+
+
+
+ +
+

4.3.12. Modeline

+
+

+Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline. Most informations I disable for it, except for the cursor information (row + column) as well as a widget for mu4e and git information. +

+ +
+
+(use-package doom-modeline
+  :init
+  (doom-modeline-mode)
+  (column-number-mode)
+  :custom
+  ((doom-modeline-height 22)
+   (doom-modeline-indent-info nil)
+   (doom-modeline-buffer-encoding nil)))
+
+
+
+
+
+
+ +
+

4.3.13. Helper Modes

+
+
+
    +
  1. Vertico, Orderless, Marginalia, Consult, Embark
    +
    +

    +This set of packages uses the default emacs completion framework and works together to provide a very nice user experience: +

    + +
      +
    • Vertico simply provides a vertically stacking completion
    • +
    • Marginalia adds more information to completion results
    • +
    • Orderless allows for fuzzy matching
    • +
    • Consult provides better implementations for several user functions, e.g. consult-line or consult-outline
    • +
    • Embark allows acting on the results in the minibuffer while the completion is still ongoing - this is extremely useful since it allows to, for example, read the documentation for several functions without closing the help search. It can also collect the results of a grep operation into a seperate buffer that edits the result in their original location.
    • +
    + +

    +Nerd icons is originally enabled here: Icons +

    +
    + +
      +
    1. vertico
      +
      +
      +
      +(setq read-buffer-completion-ignore-case t
      +      read-file-name-completion-ignore-case t
      +      completion-ignore-case t)
      +
      +(use-package vertico
      +  :custom
      +  (vertico-scroll-margin 0)
      +  (vertico-count 10)
      +  (vertico-resize t)
      +  (vertico-cycle t)
      +  :init
      +  (vertico-mode)
      +  (vertico-mouse-mode))
      +
      +
      +
      +
    2. + +
    3. vertico-directory
      +
      +

      +This package allows for Ido-like directory navigation. +

      + +
      +
      (use-package vertico-directory
      +  :ensure nil
      +  :after vertico
      +  :bind (:map vertico-map
      +              ("RET" . vertico-directory-enter)
      +              ("C-DEL" . vertico-directory-delete-word)
      +              ("DEL" . vertico-directory-delete-char))
      +  ;; Tidy shadowed file names
      +  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
      +
      +
      +
      +
      +
    4. + +
    5. orderless
      +
      +

      +The completion styles that I chose here can possibly still be improved. I need to spend more time on this. +

      + +
      +
      +(use-package orderless
      +  :custom
      +  (completion-styles '(orderless flex basic))
      +  (completion-category-overrides '((file (styles . (partial-completion)))
      +                                   (eglot (styles orderless)))))
      +
      +
      +
      +
      +
    6. + +
    7. consult
      +
      +

      +The big winner here are the convenient keybinds being setup here for general use. Also, I setup vim-navigation for minibuffer completions. consult-buffer is set twice because I am still used to that weird C-M-j command that I chose for ivy-switch-buffer when I first started using Emacs. I want to move to the other command but for now it is not feasible to delete the other one. +

      + +
      +
      +(use-package consult
      +  :config
      +  (setq consult-fontify-max-size 1024)
      +  :bind
      +  (("C-x b" . consult-buffer)
      +   ("C-c <C-m>" . consult-global-mark)
      +   ("C-c C-a" . consult-org-agenda)
      +   ("C-x O" . consult-org-heading)
      +   ("C-M-j" . consult-buffer)
      +   ("C-s" . consult-line)
      +   ("M-g M-g" . consult-goto-line)
      +   ("M-g i" . consult-imenu)
      +   ("M-s M-s" . consult-line-multi)
      +   :map minibuffer-local-map
      +   ("C-j" . next-line)
      +   ("C-k" . previous-line)))
      +
      +
      +
      +
      +
    8. +
    9. embark
      +
      +

      +I have stripped down the embark keybinds heavily. It is very useful to me even in it's current state, but it quickly becomes overwhelming. embark-dwim acts on a candidate without closing the minibuffer, which is very useful. embark-act lets the user choose from all actions, but has an overwhelming interface. +

      + +
      +
      +(use-package embark
      +  :bind
      +  (("C-." . embark-act)
      +   ("M-." . embark-dwim)
      +   ("C-h B" . embark-bindings)
      +   ("C-c c" . embark-collect))
      +  :custom
      +  (prefix-help-command #'embark-prefix-help-command)
      +  (embark-quit-after-action '((t . nil)))
      +  :config
      +  (add-to-list 'display-buffer-alist
      +               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
      +                 nil
      +                 (window-parameters (mode-line-format . none)))))
      +
      +
      +
      +
      +
    10. +
    11. embark-consult
      +
      +

      +Provides previews for embark. +

      + +
      +
      +(use-package embark-consult
      +  :after (embark consult)
      +  :demand t ; only necessary if you have the hook below
      +  ;; if you want to have consult previews as you move around an
      +  ;; auto-updating embark collect buffer
      +  :hook
      +  (embark-collect-mode . consult-preview-at-point-mode))
      +
      +
      +
      +
    12. +
    13. marginalia
      +
      +

      +I set the annotation-mode of marginalia to heavy. This gives even more information on the stuff that you are looking at. One thing I am missing from ivy is the highlighting on mode-commands based on the current state of the mode. Also, I do not understand all the shorthands used by marginalia yet. +

      + +
      +
      (use-package marginalia
      +  :after vertico
      +  :init
      +  (marginalia-mode)
      +  (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)))
      +
      +
      +
      +
      +
    14. +
    15. nerd-icons-completion
      +
      +

      +As stated above, this simply provides nerd-icons to the completion framework. +

      + +
      +
      +(use-package nerd-icons-completion
      +  :after (marginalia nerd-icons)
      +  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
      +  :init
      +  (nerd-icons-completion-mode))
      +
      +
      +
      +
      +
      +
    16. +
    +
  2. + +
  3. Helpful + which-key: Better help defaults
    +
    +

    +This pair of packages provides information on keybinds in addition to function names, which makes it easier to remember keybinds (which-key). The helpful package provides a better Help framework for Emacs. For some reason, the Help windows are always being focused by the cursor even though I have set help-window-select to nil. I do not understand why. +

    + +
    +
    +(use-package which-key
    +  :init (which-key-mode)
    +  :diminish which-key-mode
    +  :config
    +  (setq which-key-idle-delay 0.3))
    +
    +(use-package helpful
    +  :bind
    +  (("C-h f" . helpful-callable)
    +   ("C-h v" . helpful-variable)
    +   ("C-h k" . helpful-key)
    +   ("C-h C-." . helpful-at-point))
    +  :config
    +  (setq help-window-select nil))
    +
    +
    +
    +
  4. +
+
+ +
+

4.3.14. Ligatures

+
+

+Personally, I think ligatures are fancy. With this mode, they stay 'cursorable'. However, I do not need them in all modes, so I only use them in programming modes. +

+ +
+
+(use-package ligature
+  :init
+  (global-ligature-mode t)
+  :config
+  (ligature-set-ligatures 'prog-mode
+                          '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
+                            ":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
+                            "!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
+                            "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
+                            "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
+                            "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
+                            "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
+                            "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
+                            ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
+                            "<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
+                            "##" "#(" "#?" "#_" "%%" ".=" ".." ".?" "+>" "++" "?:" "?="
+                            "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\"
+                            "://" ";;")))
+
+
+
+
+
+ +
+

4.3.15. Popup (popper) + Shackle Buffers

+
+

+The popper package allows to declare different buffers as 'popup-type', which sort of acts like a scratchpad. It can be toggled at any time using popper-toggle and the resulting frame can be freely customized (with shackle) to a certain size. It is also possible to prevent a buffer from appearing - I do this for example to the *Warnings* buffer, since usually I am not interested in it's output. +

+ +

+popper-echo-mode shows all buffers that are currently stored as a popup in the echo area when a popup is opened - this is useful since you can cycle between all popup buffers. +

+ +
+
+(use-package popper
+  :bind (("M-["   . popper-toggle))
+  :init
+  (setq popper-reference-buffers
+        '("\\*Messages\\*"
+          ("\\*Warnings\\*" . hide)
+          "Output\\*$"
+          "\\*Async Shell Command\\*"
+          "\\*Async-native-compile-log\\*"
+          help-mode
+          helpful-mode
+          "*Occur*"
+          "*scratch*"
+          "*julia*"
+          "*Python*"
+          ;; ("*tex-shell*" . hide)
+          (compilation-mode . hide)))
+  (popper-mode +1)
+  (popper-echo-mode +1))
+
+(use-package shackle
+  :config
+  (setq shackle-rules '(("*Messages*" :select t :popup t :align right :size 0.3)
+                        ("*Warnings*" :ignore t :popup t :align right :size 0.3)
+                        ("*Occur*" :select t :popup t :align below :size 0.2)
+                        ("*scratch*" :select t :popup t :align below :size 0.2)
+                        ("*Python*" :select t :popup t :align below :size 0.2)
+                        ("*tex-shell*" :ignore t :popup t :align below :size 0.2)
+                        (helpful-mode :select t :popup t :align right :size 0.35)
+                        (help-mode :select t :popup t :align right :size 0.4)))
+  (shackle-mode 1))
+
+
+
+
+
+
+

4.3.16. Indicate first and last line of buffer

+
+

+This places little angled indicators on the fringe of a window which indicate buffer boundaries. This is not super useful, but makes use of a space that I want to keep for aesthetic reasons anyways and makes it a bit more useful in the process. +

+ +
+
+(setq-default indicate-buffer-boundaries t)
+
+
+
+
+
+
+

4.3.17. Authentication

+
+

+This defines the authentication sources used by org-calfw (Calendar) and Forge. +

+ +
+
+(setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg")
+      auth-source-cache-expiry nil) ; default is 2h
+
+
+
+
+
+
+
+

4.4. Modules

+
+

+This section houses all configuration bits that are related to a specific package that is not fundamental to my Emacs experience. +

+ +

+At some point this will receive further sorting, but for now this is good enough. +

+
+ +
+

4.4.1. Org Mode

+
+

+org-mode is probably my most-used mode in Emcas. It acts as my organizer, config management tool and calender even. +

+ +

+Note that nearly all headings within the Org-mode heading are coded within the use-package setup, so be very careful about moving stuff about here. +

+
+ +
    +
  1. General org-mode
    +
    +

    +This sets up the basic org-mode. I wrote a function to handle some of the initial org-mode behaviour in org-mode setup. + +This part of the configuration mostly makes some aesthetic changes, enables neat LaTeX and points Emacs to some files that it needs for org-mode +

    + +
    +
    +(use-package org
    +  ;;:diminish (org-indent-mode)
    +  :hook (org-mode . swarsel/org-mode-setup)
    +  :bind
    +  (("C-<tab>" . org-fold-outer)
    +  ("C-c s" . org-store-link))
    +  :config
    +  (setq org-ellipsis " ⤵"
    +        org-link-descriptive t
    +        org-hide-emphasis-markers t)
    +  (setq org-startup-folded t)
    +  (setq org-support-shift-select t)
    +
    +  ;; (setq org-agenda-start-with-log-mode t)
    +  ;; (setq org-log-done 'time)
    +  ;; (setq org-log-into-drawer t)
    +  (setq org-startup-with-inline-images t)
    +  (setq org-image-actual-width nil)
    +  (setq org-format-latex-options '(:foreground "White" :background default :scale 2.0 :html-foreground "Black" :html-background "Transparent" :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\[")))
    +
    +
    +
    +
    +
  2. +
  3. org-agenda
    +
    +

    +Here I setup a plethora of keywords, keybinds and paths to give my org-agenda more power. +

    + +
    +
    +(setq org-agenda-files '("/home/swarsel/Nextcloud/Org/Tasks.org"
    +                         "/home/swarsel/Nextcloud/Org/Archive.org"
    +                         "/home/swarsel/Nextcloud/Org/Anki.org"
    +                         "/home/swarsel/Calendars/leon_cal.org"))
    +
    +(setq org-refile-targets
    +      '((swarsel-archive-org-file :maxlevel . 1)
    +        (swarsel-anki-org-file :maxlevel . 1)
    +        (swarsel-tasks-org-file :maxlevel . 1)))
    +
    +(setq org-todo-keywords
    +      '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
    +        (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)")))
    +
    +
    +;; Configure custom agenda views
    +(setq org-agenda-custom-commands
    +      '(("d" "Dashboard"
    +         ((agenda "" ((org-deadline-warning-days 7)))
    +          (todo "NEXT"
    +                ((org-agenda-overriding-header "Next Tasks")))
    +          (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))
    +
    +        ("n" "Next Tasks"
    +         ((todo "NEXT"
    +                ((org-agenda-overriding-header "Next Tasks")))))
    +
    +        ("W" "Work Tasks" tags-todo "+work-email")
    +
    +
    +        ("w" "Workflow Status"
    +         ((todo "WAIT"
    +                ((org-agenda-overriding-header "Waiting on External")
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "REVIEW"
    +                ((org-agenda-overriding-header "In Review")
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "PLAN"
    +                ((org-agenda-overriding-header "In Planning")
    +                 (org-agenda-todo-list-sublevels nil)
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "BACKLOG"
    +                ((org-agenda-overriding-header "Project Backlog")
    +                 (org-agenda-todo-list-sublevels nil)
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "READY"
    +                ((org-agenda-overriding-header "Ready for Work")
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "ACTIVE"
    +                ((org-agenda-overriding-header "Active Projects")
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "COMPLETED"
    +                ((org-agenda-overriding-header "Completed Projects")
    +                 (org-agenda-files org-agenda-files)))
    +          (todo "CANC"
    +                ((org-agenda-overriding-header "Cancelled Projects")
    +                 (org-agenda-files org-agenda-files)))))))
    +
    +
    +
    +
    +
    +
  4. +
  5. org capture templates
    +
    +

    +I wrote these capture templates to allow myself to quickly create Anki cards from within Emacs. I nearly never use this feature, but it cannot hurt to have. +

    + +
    +
    +(setq org-capture-templates
    +      `(
    +        ("a" "Anki basic"
    +         entry
    +         (file+headline swarsel-org-anki-filepath "Dispatch")
    +         (function swarsel-anki-make-template-string))
    +
    +        ("A" "Anki cloze"
    +         entry
    +         (file+headline org-swarsel-anki-file "Dispatch")
    +         "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: Cloze\n:ANKI_DECK: 🦁 All::01 ❤️ Various::00 ✨ Allgemein\n:END:\n** Text\n%?\n** Extra\n")
    +        ("t" "Tasks / Projects")
    +        ("tt" "Task" entry (file+olp swarsel-org-tasks-filepath "Inbox")
    +         "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)
    +        ))
    +)
    +
    +
    +
    +
  6. + +
  7. Font Faces
    +
    +

    +Again, my understanding of the font-faces in Emacs is limited. This is mostly just tuned so that my org-files look acceptable. +

    + +
    +
    +
    +
    +;; Set faces for heading levels
    +(with-eval-after-load 'org-faces  (dolist (face '((org-level-1 . 1.1)
    +                                                  (org-level-2 . 0.9)
    +                                                  (org-level-3 . 0.9)
    +                                                  (org-level-4 . 0.9)
    +                                                  (org-level-5 . 0.9)
    +                                                  (org-level-6 . 0.9)
    +                                                  (org-level-7 . 0.9)
    +                                                  (org-level-8 . 0.9)))
    +                                    (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
    +
    +                      ;; Ensure that anything that should be fixed-pitch in Org files appears that way
    +                      (set-face-attribute 'org-block nil   :inherit 'fixed-pitch)
    +                      (set-face-attribute 'org-table nil   :inherit 'fixed-pitch)
    +                      (set-face-attribute 'org-formula nil   :inherit 'fixed-pitch)
    +                      (set-face-attribute 'org-code nil :inherit '(shadow 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))
    +
    +
    +
    +
    +
  8. + +
  9. org-appear
    +
    +

    +This package makes emphasis-markers appear when the cursor moves over them. Very useful as I enjoy the clean look of not always seeing them, but it is annoying not to be able to edit them properly. +

    + +
    +
    +(use-package org-appear
    +  :hook (org-mode . org-appear-mode)
    +  :init
    +  (setq org-appear-autolinks t)
    +  (setq org-appear-autokeywords t)
    +  (setq org-appear-autoentities t)
    +  (setq org-appear-autosubmarkers t))
    +
    +
    +
    +
    +
  10. + +
  11. Centered org-mode Buffers
    +
    +

    +I like org-mode buffers to be centered, as I do not find that enormous lines are of big use. +

    + +

    +Function definition in: Visual-fill column +

    + +
    +
    +(use-package visual-fill-column
    +  :hook (org-mode . swarsel/org-mode-visual-fill))
    +
    +
    +
    +
    +
  12. + +
  13. Fix headings not folding sometimes
    +
    +

    +There is a weird bug in org-mode that makes it so that headings were not folding correctly sometimes. This setting seems to fix it. +

    + +
    +
    +(setq org-fold-core-style 'overlays)
    +
    +
    +
    +
    +
  14. + +
  15. Babel
    +
    +

    +org-babel allows to run blocks in other programming languages within an org-mode buffer, similar to what e.g. jupyterhub offers for python. +

    + +

    +It also offers a very useful utility of exporting org-mode buffers to different formats; the feature I enjoy most is what makes this file useful: the tangling functionality. +

    +
    + +
      +
    1. Language Configuration
      +
      +
        +
      • This configures the languages that babel recognizes.
      • +
      + +
      +
      +(org-babel-do-load-languages
      + 'org-babel-load-languages
      + '((emacs-lisp . t)
      +   (python . t)
      +   (js . t)
      +   (shell . t)
      +   ))
      +
      +(push '("conf-unix" . conf-unix) org-src-lang-modes)
      +
      +
      +
      +
      +
    2. + +
    3. old easy structure templates
      +
      +
        +
      • +org 9.2 changed the way structure templates work. This brings back the old way it worked. +

        + +

        +Usage: Type <, followed by one of the below keywords and press RET. The corresponding source block should appear. +

        + +
        +
        +(require 'org-tempo)
        +(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
        +(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
        +(add-to-list 'org-structure-template-alist '("py" . "src python :results output"))
        +(add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle"))
        +
        +
        +
      • +
      +
      +
    4. +
    +
  16. + +
  17. aucTex
    +
    +

    +This provides several utilities for LaTeX in Emacs, including many completions and convenience functions for math-mode. +

    + +
    +
    +(use-package auctex)
    +(setq TeX-auto-save t)
    +(setq TeX-save-query nil)
    +(setq TeX-parse-self t)
    +  (setq-default TeX-master nil)
    +
    +(add-hook 'LaTeX-mode-hook 'visual-line-mode)
    +(add-hook 'LaTeX-mode-hook 'flyspell-mode)
    +(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
    +(add-hook 'LaTeX-mode-hook 'reftex-mode)
    +(setq LaTeX-electric-left-right-brace t)
    +(setq font-latex-fontify-script nil)
    +(setq TeX-electric-sub-and-superscript t)
    +  ;; (setq reftex-plug-into-AUCTeX t)
    +
    +
    +
    +
    +
  18. + +
  19. org-download
    +
    +

    +This package allows to download and copy images into org-mode buffers. Sadly it does not work in a very stable manner - if you copy images that are also links to another page (like is often the case in a Google image search), Emacs might crash from this. +

    + +
    +
    +(use-package org-download
    +  :after org
    +  :defer nil
    +  :custom
    +  (org-download-method 'directory)
    +  (org-download-image-dir "./images")
    +  (org-download-heading-lvl 0)
    +  (org-download-timestamp "org_%Y%m%d-%H%M%S_")
    +  ;;(org-image-actual-width 500)
    +  (org-download-screenshot-method "grim -g \"$(slurp)\" %s")
    +  :bind
    +  ("C-M-y" . org-download-screenshot)
    +  :config
    +  (require 'org-download))
    +
    +
    +
    +
    +
  20. + +
  21. org-fragtog
    +
    +

    +This package automatically toggles LaTeX-fragments in org-files. It seems to also work in markdown-files which is a nice addition, as my Obsidian notes are held in markdown. +

    + +
    +
    +(use-package org-fragtog)
    +(add-hook 'org-mode-hook 'org-fragtog-mode)
    +(add-hook 'markdown-mode-hook 'org-fragtog-mode)
    +
    +
    +
    +
    +
  22. + +
  23. org-modern
    +
    +

    +This just makes org-mode a little bit more beautiful, mostly by making the begin_src and end_src tags in source-blocks turn into more beautiful icons, as well as hiding #+ tags before them, as well as in the properties section of the file. +

    + +
    +
    +(use-package org-modern
    +  :config (setq org-modern-block-name
    +                '((t . t)
    +                  ("src" "»" "∥")))
    +  :hook (org-mode . org-modern-mode))
    +
    +
    +
    +
    +
  24. + +
  25. Presentations
    +
    +

    +Recently I have grown fond of holding presentations using Emacs :) +

    + +
    +
    +    (use-package org-present
    +    :bind (:map org-present-mode-keymap
    +           ("q" . org-present-quit)
    +           ("<left>" . swarsel/org-present-prev)
    +           ("<up>" . 'ignore)
    +           ("<down>" . 'ignore)
    +           ("<right>" . swarsel/org-present-next))
    +    :hook ((org-present-mode . swarsel/org-present-start)
    +           (org-present-mode-quit . swarsel/org-present-end))
    +    )
    +
    +
    +    (use-package hide-mode-line)
    +
    +    (defun swarsel/org-present-start ()
    +      (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-verbatim (:height 1.55) org-verbatim)
    +                                         (org-block (:height 1.25) org-block)
    +                                         (org-block-begin-line (:height 0.7) org-block)
    +                                         ))
    +      (dolist (face '((org-level-1 . 1.1)
    +                                                    (org-level-2 . 1.2)
    +                                                    (org-level-3 . 1.2)
    +                                                    (org-level-4 . 1.2)
    +                                                    (org-level-5 . 1.2)
    +                                                    (org-level-6 . 1.2)
    +                                                    (org-level-7 . 1.2)
    +                                                    (org-level-8 . 1.2)))
    +                                      (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
    +
    +      (setq header-line-format " ")
    +      (setq visual-fill-column-width 90)
    +      (setq indicate-buffer-boundaries nil)
    +      (setq inhibit-message nil)
    +      (breadcrumb-mode 0)
    +      (org-display-inline-images)
    +      (global-hl-line-mode 0)
    +      (display-line-numbers-mode 0)
    +      (org-modern-mode 0)
    +      (evil-insert-state 1)
    +      (beginning-of-buffer)
    +      (org-present-read-only)
    +      ;; (org-present-hide-cursor)
    +      (swarsel/org-present-slide)
    +      )
    +
    +    (defun swarsel/org-present-end ()
    +           (setq-local face-remapping-alist '((default variable-pitch default)))
    +           (dolist (face '((org-level-1 . 1.1)
    +                                                    (org-level-2 . 0.9)
    +                                                    (org-level-3 . 0.9)
    +                                                    (org-level-4 . 0.9)
    +                                                    (org-level-5 . 0.9)
    +                                                    (org-level-6 . 0.9)
    +                                                    (org-level-7 . 0.9)
    +                                                    (org-level-8 . 0.9)))
    +                                      (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
    +           (setq header-line-format nil)
    +           (setq visual-fill-column-width 150)
    +           (setq indicate-buffer-boundaries t)
    +           (setq inhibit-message nil)
    +           (breadcrumb-mode 1)
    +           (global-hl-line-mode 1)
    +           (display-line-numbers-mode 1)
    +           (org-remove-inline-images)
    +           (org-modern-mode 1)
    +           (evil-normal-state 1)
    +           ;; (org-present-show-cursor)
    +           )
    +
    +  (defun swarsel/org-present-slide ()
    +    (org-overview)
    +    (org-show-entry)
    +    (org-show-children)
    +      )
    +
    +  (defun swarsel/org-present-prev ()
    +    (interactive)
    +    (org-present-prev)
    +    (swarsel/org-present-slide))
    +
    +  (defun swarsel/org-present-next ()
    +    (interactive)
    +    (unless (eobp)
    +    (org-next-visible-heading 1)
    +    (org-fold-show-entry))
    +    (when (eobp)
    +    (org-present-next)
    +    (swarsel/org-present-slide)
    +    ))
    +
    +(defun clojure-leave-clojure-mode-function ()
    + )
    +
    +(add-hook 'buffer-list-update-hook #'clojure-leave-clojure-mode-function)
    +    (add-hook 'org-present-mode-hook 'swarsel/org-present-start)
    +    (add-hook 'org-present-mode-quit-hook 'swarsel/org-present-end)
    +    (add-hook 'org-present-after-navigate-functions 'swarsel/org-present-slide)
    +
    +
    +
    +
    +
  26. +
+
+
+

4.4.2. Nix Mode

+
+

+This adds a rudimentary nix-mode to Emacs. I have not really tried this out, as I am mostly editing nix-files in org-mode anyways. +

+ +
+
+(use-package nix-mode
+  :mode "\\.nix\\'")
+
+
+
+
+
+ +
+

4.4.3. Markdown Mode

+
+
+
    +
  1. Mode
    +
    +
    +
    +(setq markdown-command "pandoc")
    +
    +(use-package markdown-mode
    +  :ensure t
    +  :mode ("README\\.md\\'" . gfm-mode)
    +  :init (setq markdown-command "multimarkdown")
    +  :bind (:map markdown-mode-map
    +              ("C-c C-e" . markdown-do)))
    +
    +
    +
    +
    +
  2. + +
  3. LaTeX in Markdown
    +
    +
    +
    +(add-hook 'markdown-mode-hook
    +          (lambda ()
    +            (local-set-key (kbd "C-c C-x C-l") 'org-latex-preview)
    +            (local-set-key (kbd "C-c C-x C-u") 'markdown-toggle-url-hiding)
    +            ))
    +
    +
    +
    +
    +
  4. +
+
+ +
+

4.4.4. Olivetti

+
+

+Olivetti is a mode specialized for writing prose in Emacs. I went for a very simple setup with little distractions. +

+ +

+This mode is not automatically activated anywhere because I only rarely need it. +

+ +
+
+(use-package olivetti
+  :init
+  (setq olivetti-body-width 100)
+  (setq olivetti-recall-visual-line-mode-entry-state t))
+
+
+
+
+
+ +
+

4.4.5. darkroom

+
+

+Darkroom is package that reduces all forms of distraction to a minimum - this can be useful when simply reading a file for example. For this mode I have increased the text scale by a large margin to make for comfortable reading +This mode is not automatically activated anywhere because I only rarely need it. +

+ +
+
+(use-package darkroom
+  :init
+  (setq darkroom-text-scale-increase 3))
+
+
+
+
+
+ +
+

4.4.6. Ripgrep

+
+

+This is the ripgrep command for Emacs. +

+ +
+
+(use-package rg)
+
+
+
+
+
+
+

4.4.7. Tree-sitter

+
+

+Tree-sitter is a parsing library integrated into Emacs to provide better syntax highlighting and code analysis. It generates concrete syntax trees for source code, enabling more accurate and efficient text processing. Emacs' tree-sitter integration enhances language support, offering features like incremental parsing and precise syntax-aware editing. This improves the development experience by providing robust and dynamic syntax features, making it easier for me to navigate and manipulate code. +

+ +

+In order to update the language grammars, run the next command below. +

+ +
+
+(mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
+
+
+
+ +
+
+(use-package emacs
+  :ensure nil
+  :init
+  (setq treesit-language-source-alist
+        '((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
+          (c . ("https://github.com/tree-sitter/tree-sitter-c"))
+          (cmake . ("https://github.com/uyha/tree-sitter-cmake"))
+          (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
+          (css . ("https://github.com/tree-sitter/tree-sitter-css"))
+          (elisp . ("https://github.com/Wilfred/tree-sitter-elisp"))
+          (go . ("https://github.com/tree-sitter/tree-sitter-go"))
+          (html . ("https://github.com/tree-sitter/tree-sitter-html"))
+          (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
+          (json . ("https://github.com/tree-sitter/tree-sitter-json"))
+          (julia . ("https://github.com/tree-sitter/tree-sitter-julia"))
+          (latex . ("https://github.com/latex-lsp/tree-sitter-latex"))
+          (make . ("https://github.com/alemuller/tree-sitter-make"))
+          (markdown . ("https://github.com/ikatyang/tree-sitter-markdown"))
+          (R . ("https://github.com/r-lib/tree-sitter-r"))
+          (python . ("https://github.com/tree-sitter/tree-sitter-python"))
+          (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "typescript/src" "typescript"))
+          (rust . ("https://github.com/tree-sitter/tree-sitter-rust"))
+          (sql . ("https://github.com/m-novikov/tree-sitter-sql"))
+          (toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
+          (tsx  . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src"))
+          (yaml . ("https://github.com/ikatyang/tree-sitter-yaml"))))
+  )
+
+(use-package treesit-auto
+  :config
+  (global-treesit-auto-mode)
+  (setq treesit-auto-install 'prompt))
+
+
+
+
+
+
+ +
+

4.4.8. direnv

+
+
+
+(use-package direnv
+  :custom (direnv-always-show-summary nil)
+  :config (direnv-mode))
+
+
+
+
+
+ +
+

4.4.9. avy

+
+

+avy provides the ability to search for any character on the screen (not only in the current buffer!) - I enjoy this utility a lot and use it possibly even more often than the native vim commands. +

+ +
+
+(use-package avy
+  :bind
+  (("M-o" . avy-goto-char-timer))
+  :config
+  (setq avy-all-windows 'all-frames))
+
+
+
+
+
+ +
+

4.4.10. crdt (Collaborative Editing)

+
+

+With this it is possible to work on the same file collaboratively. I have never tried it out, but it sounds cool. +

+ +
+
+(use-package crdt)
+
+
+
+
+
+ +
+

4.4.11. devdocs

+
+

+devdocs is a very nice package that provides documentation from https:devdocs.io. This is very useful since e.g. pyright provides only a very bad documentation and I do not want to leave Emacs all the time just to read documentation. +

+ +

+To install a documentation, use the devdocs=install command and select the appropriate version. devdocs-update-all can be used to download and reinstall all installed documents if a newer version is available. Check documentation with devdocs-lookup (C-SPC h d). +

+ +
+
+(use-package devdocs)
+
+(add-hook 'python-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1"))))
+(add-hook 'python-ts-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("python~3.12" "numpy~1.23" "matplotlib~3.7" "pandas~1"))))
+
+(add-hook 'c-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("c"))))
+(add-hook 'c-ts-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("c"))))
+
+(add-hook 'c++-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("cpp"))))
+(add-hook 'c++-ts-mode-hook
+        (lambda () (setq-local devdocs-current-docs '("cpp"))))
+
+(devdocs-update-all)
+
+
+
+
+
+ +
+

4.4.12. Projectile

+
+

+projectile is useful for keeping track of your git projects within Emacs. I mostly use it to quickly switch between projects. +

+ +
+
+(use-package projectile
+  :diminish projectile-mode
+  :config (projectile-mode)
+  :custom ((projectile-completion-system 'auto)) ;; integrate ivy into completion system
+  :bind-keymap
+  ("C-c p" . projectile-command-map) ; all projectile commands under this
+  :init
+  ;; NOTE: Set this to the folder where you keep your Git repos!
+  (when (file-directory-p swarsel-projects-directory)
+    (setq projectile-project-search-path (list swarsel-projects-directory)))
+(setq projectile-switch-project-action #'magit-status))
+
+
+
+
+
+ +
+

4.4.13. Magit

+
+

+magit is the best git utility I have ever used - it has a beautiful interface and is very verbose. Here I mostly just setup the list of repositories that I want to expost to magit. +

+ +

+Also, Emacs needs a little extra love to accept my Yubikey for git commits etc. We also set that here. +

+ +
+
+(use-package magit
+  :config
+  (setq magit-repository-directories `((,swarsel-projects-directory  . 1)
+                                       (,swarsel-emacs-directory . 0)
+                                       (,swarsel-obsidian-directory . 0)
+                                       ("~/.dotfiles/" . 0)))
+  :custom
+  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ; stay in the same window
+
+;; yubikey support for pushing commits
+;; commiting is enabled through nixos gpg-agent config
+(setq epg-pinentry-mode 'loopback)
+(setenv "SSH_AUTH_SOCK" (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))
+
+
+
+
+ +
+

4.4.14. Forge

+
+

+NOTE: Make sure to configure a GitHub token before using this package! +

+ + +
+
+(use-package forge
+  :after magit)
+
+(with-eval-after-load 'forge
+  (add-to-list 'forge-alist
+               '("sgit.iue.tuwien.ac.at"
+                 "sgit.iue.tuwien.ac.at/api/v1"
+                 "sgit.iue.tuwien.ac.at"
+                 forge-gitea-repository)))
+
+
+
+
+ +
+

4.4.15. git-timemachine

+
+

+This is just a nice utility to browse different versions of a file of a git project within Emacs. +

+ +
+
+(use-package git-timemachine
+   :hook (git-time-machine-mode . evil-normalize-keymaps)
+   :init (setq git-timemachine-show-minibuffer-details t))
+
+
+
+
+
+ +
+

4.4.16. Delimiters (brackets): rainbow-delimiters, highlight-parentheses

+
+
    +
  • rainbow-delimiters colors all delimiters, also ones not in current selection
  • +
  • paren highlights the current delimiter selection especially bold
  • +
  • highlight-parentheses boldly highlights all delimiters in current selection
  • +
+ +

+I am not completely sure on electric-pair-mode yet, sometimes it is very helpful, sometimes it annoys me to no end. +

+ +
+
+  (use-package rainbow-delimiters
+    :hook (prog-mode . rainbow-delimiters-mode))
+
+  (use-package highlight-parentheses
+    :config
+    (setq highlight-parentheses-colors '("black" "white" "black" "black" "black" "black" "black"))
+    (setq highlight-parentheses-background-colors '("magenta" "blue" "cyan" "green" "yellow" "orange" "red"))
+    (global-highlight-parentheses-mode t))
+
+  (electric-pair-mode 1)
+  (setq electric-pair-preserve-balance nil)
+  ;; don't try to be overly smart
+  (setq electric-pair-delete-adjacent-pairs nil)
+  ;; don't skip newline when auto-pairing parenthesis
+  (setq electric-pair-skip-whitespace-chars '(9 32))
+
+  ;; in org-mode buffers, do not pair < and > in order not to interfere with org-tempo
+(add-hook 'org-mode-hook (lambda ()
+           (setq-local electric-pair-inhibit-predicate
+                   `(lambda (c)
+                  (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c))))))
+
+
+
+
+
+ +
+

4.4.17. rainbow-mode

+
+

+Complimentary to the delimiters-packages above, this package sets the background color of the delimiters, which makes it easier to see at a glance where we are in a delimiter-tree. +

+ +
+
+(use-package rainbow-mode
+  :config (rainbow-mode))
+
+
+
+
+
+
+

4.4.18. Corfu

+
+

+This is the company equivalent to the vertico gang. +I dislike the standard behaviour that makes the cursor move into the completion framework on presses of <up> and <down>. +

+ +

+Nerd icons is originally enabled here: Icons +

+ +

+Navigation functions defined here: corfu: Do not interrupt navigation +

+ +
+
+;; (use-package corfu
+;;   :custom
+;;   (corfu-cycle t)
+;;   :init
+;;   (global-corfu-mode))
+
+(use-package corfu
+  :init
+  (global-corfu-mode)
+  (corfu-history-mode)
+  (corfu-popupinfo-mode) ; Popup completion info
+  :custom
+  (corfu-auto t)
+  (corfu-auto-prefix 3)
+  (corfu-auto-delay 0.3)
+  (corfu-cycle t)
+  (corfu-quit-no-match 'separator)
+  (corfu-separator ?\s)
+  ;; (corfu-quit-no-match t)
+  (corfu-popupinfo-max-height 70)
+  (corfu-popupinfo-delay '(0.5 . 0.2))
+  ;; (corfu-preview-current 'insert) ; insert previewed candidate
+  (corfu-preselect 'prompt)
+  (corfu-on-exact-match nil)      ; Don't auto expand tempel snippets
+  ;; Optionally use TAB for cycling, default is `corfu-complete'.
+  :bind (:map corfu-map
+              ("M-SPC"      . corfu-insert-separator)
+              ("<return>" . swarsel/corfu-normal-return)
+              ;; ("C-<return>" . swarsel/corfu-complete)
+              ("S-<up>" . corfu-popupinfo-scroll-down)
+              ("S-<down>" . corfu-popupinfo-scroll-up)
+              ("C-<up>" . corfu-previous)
+              ("C-<down>" . corfu-next)
+              ("<insert-state> <up>"      . swarsel/corfu-quit-and-up)
+              ("<insert-state> <down>"     . swarsel/corfu-quit-and-down))
+  )
+
+(use-package nerd-icons-corfu)
+
+(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)
+
+(setq nerd-icons-corfu-mapping
+      '((array :style "cod" :icon "symbol_array" :face font-lock-type-face)
+        (boolean :style "cod" :icon "symbol_boolean" :face font-lock-builtin-face)
+        ;; ...
+        (t :style "cod" :icon "code" :face font-lock-warning-face)))
+
+
+
+
+
+ +
+

4.4.19. cape

+
+

+cape adds even more completion capabilities by adding a lot of completion logic that is exposed as separate functions. I tried out adding these to the completion-at-points-functions alist, but I felt like it cluttered my suggestions too much. Hence I now just call the respective functions when I need them. For this I setup the C-z keybinding in General evil. +

+ +

+I leave the commented out alist extensions here in case I want to try them out at some point in the future. +

+ +
+
+(use-package cape
+  :bind
+  ("C-z p" . completion-at-point) ;; capf
+  ("C-z t" . complete-tag)        ;; etags
+  ("C-z d" . cape-dabbrev)        ;; or dabbrev-completion
+  ("C-z h" . cape-history)
+  ("C-z f" . cape-file)
+  ("C-z k" . cape-keyword)
+  ("C-z s" . cape-elisp-symbol)
+  ("C-z e" . cape-elisp-block)
+  ("C-z a" . cape-abbrev)
+  ("C-z l" . cape-line)
+  ("C-z w" . cape-dict)
+  ("C-z :" . cape-emoji)
+  ("C-z \\" . cape-tex)
+  ("C-z _" . cape-tex)
+  ("C-z ^" . cape-tex)
+  ("C-z &" . cape-sgml)
+  ("C-z r" . cape-rfc1345)
+  ;; Add to the global default value of `completion-at-point-functions' which is
+  ;; used by `completion-at-point'.  The order of the functions matters, the
+  ;; first function returning a result wins.  Note that the list of buffer-local
+  ;; completion functions takes precedence over the global list.
+  ;; (add-to-list 'completion-at-point-functions #'cape-dabbrev)
+  ;; (add-to-list 'completion-at-point-functions #'cape-file)
+  ;; (add-to-list 'completion-at-point-functions #'cape-elisp-block)
+  ;; (add-to-list 'completion-at-point-functions #'cape-history)
+  ;; (add-to-list 'completion-at-point-functions #'cape-keyword)
+  ;; (add-to-list 'completion-at-point-functions #'cape-tex)
+  ;; (add-to-list 'completion-at-point-functions #'cape-sgml)
+  ;; (add-to-list 'completion-at-point-functions #'cape-rfc1345)
+  ;; (add-to-list 'completion-at-point-functions #'cape-abbrev)
+  ;; (add-to-list 'completion-at-point-functions #'cape-dict)
+  ;; (add-to-list 'completion-at-point-functions #'cape-elisp-symbol)
+  ;; (add-to-list 'completion-at-point-functions #'cape-line)
+)
+
+
+
+
+
+ +
+

4.4.20. rust

+
+

+This is supposed to setup a rust-lsp - however, this has not worked nicely in the past and this configuration section is just a ruin really. I need to check what works and clean this up. +

+ +
+
+  ;; (use-package rustic
+  ;;   :ensure
+  ;;   :bind (:map rustic-mode-map
+  ;;               ("M-j" . lsp-ui-imenu)
+  ;;               ("M-?" . lsp-find-references)
+  ;;               ("C-c C-c l" . flycheck-list-errors)
+  ;;               ("C-c C-c a" . lsp-execute-code-action)
+  ;;               ("C-c C-c r" . lsp-rename)
+  ;;               ("C-c C-c q" . lsp-workspace-restart)
+  ;;               ("C-c C-c Q" . lsp-workspace-shutdown)
+  ;;               ("C-c C-c s" . lsp-rust-analyzer-status))
+  ;;   :config
+
+  ;;   (setq rustic-format-on-save t)
+  ;;   (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))
+
+;;   (defun rk/rustic-mode-hook ()
+;;     ;; so that run C-c C-c C-r works without having to confirm, but don't try to
+;;     ;; save rust buffers that are not file visiting. Once
+;;     ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should
+;;     ;; no longer be necessary.
+;;     (when buffer-file-name
+;;       (setq-local buffer-save-without-query t))
+;;     (add-hook 'before-save-hook 'lsp-format-buffer nil t))
+
+;; (use-package rustic
+;;   :config
+;;   (setq rustic-format-on-save t)
+;;   (setq rustic-lsp-client 'eglot)
+;;   :custom
+;;   (lsp-rust-analyzer-cargo-watch-command "clippy")
+;;   (lsp-rust-analyzer-server-display-inlay-hints t)
+;;   :mode ("\\.rs" . rustic-mode))
+
+(use-package rustic
+  :config
+  (setq rustic-format-on-save t)
+  (setq rustic-lsp-client 'eglot)
+  :mode ("\\.rs" . rustic-mode))
+
+
+
+
+ +
+

4.4.21. Tramp

+
+

+Tramp allows for SSH access of files over Emacs. I have no ideas what the options here mean, but this is a recommended configuration that I found (sadly I lost the link). I need to research more what these options really do. +

+ +
+
+
+
+(use-package tramp
+  :init
+  (setq vc-ignore-dir-regexp
+        (format "\\(%s\\)\\|\\(%s\\)"
+                vc-ignore-dir-regexp
+                tramp-file-name-regexp))
+  (setq tramp-default-method "ssh")
+  (setq tramp-auto-save-directory
+        (expand-file-name "tramp-auto-save" user-emacs-directory))
+  (setq tramp-persistency-file-name
+        (expand-file-name "tramp-connection-history" user-emacs-directory))
+  (setq password-cache-expiry nil)
+  (setq tramp-use-ssh-controlmaster-options nil)
+  (setq remote-file-name-inhibit-cache nil)
+  :config
+  (customize-set-variable 'tramp-ssh-controlmaster-options
+                          (concat
+                           "-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p "
+                           "-o ControlMaster=auto -o ControlPersist=yes"))
+)
+
+
+
+
+
+
+
+ +
+

4.4.22. diff-hl

+
+

+This is a simple highlighting utility that uses the margin to visually show the differences since the last git commit. +

+ +
+
+(use-package diff-hl
+  :hook
+  ((prog-mode
+    org-mode) . diff-hl-mode)
+  :init
+  (diff-hl-flydiff-mode)
+  (diff-hl-margin-mode)
+  (diff-hl-show-hunk-mouse-mode))
+
+
+
+
+
+
+

4.4.23. Commenting

+
+

+This package allows for swift commenting out and in of code snippets. For some reason, it is a bit broken in my config, as it sometimes comments out too much, sometimes too little, and sometimes it splits lines during commenting. Also, in org-mode when inside a src-block, it often times jumps to the top of the block. +

+ +

+Still, this is avery convenient package. +

+ +
+
+(use-package evil-nerd-commenter
+  :bind ("M-/" . evilnc-comment-or-uncomment-lines))
+
+
+
+
+
+ +
+

4.4.24. yasnippet

+
+

+yasnippet allows to define snippets that can be quickly expanded by hitting the TAB key after inputting a keyword. +

+ +

+I used to run this together with the yasnippet-snippets package, but the snippets in there I did not find all too useful for myself. I need to create some custom snippets here one day. +

+ +
+
+(use-package yasnippet
+  :init (yas-global-mode 1)
+  :config
+  (yas-reload-all))
+
+
+
+
+ +
    +
  1. yasnippet math-snippets
    +
    +

    +The following block is mostly inspired from https://code.kulupu.party/thesuess/WTFmacs/ and sets up a few prefixes that make LaTeX-math-mode nicer to use even with auctex and cape enabled. +

    + +
    +
    +
    +(setq wtf/latex-mathbb-prefix "''")
    +(setq swarsel/latex-mathcal-prefix "``")
    +
    +(use-package yasnippet
    +  :config
    +
    +  (setq wtf/english-alphabet
    +        '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"))
    +
    +  (dolist (elem wtf/english-alphabet)
    +    (when (string-equal elem (downcase elem))
    +      (add-to-list 'wtf/english-alphabet (upcase elem))))
    +
    +
    +  (yas-define-snippets
    +   'latex-mode
    +   (mapcar
    +    (lambda (elem)
    +      (list (concat wtf/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
    +    wtf/english-alphabet))
    +
    +  (yas-define-snippets
    +   'latex-mode
    +   (mapcar
    +    (lambda (elem)
    +      (list (concat swarsel/latex-mathcal-prefix elem) (concat "\\mathcal{" elem "}") (concat "Mathcal letter " elem)))
    +    wtf/english-alphabet))
    +
    +  (setq swtf/latex-math-symbols
    +        '(("x" . "\\times")
    +          ("*" . "\\cdot")
    +          ("." . "\\ldots")
    +          ("op" . "\\operatorname{$1}$0")
    +          ("o" . "\\circ")
    +          ("V" . "\\forall")
    +          ("v" . "\\vee")
    +          ("w" . "\\wedge")
    +          ("q" . "\\quad")
    +          ("f" . "\\frac{$1}{$2}$0")
    +          ("s" . "\\sum_{$1}^{$2}$0")
    +          ("p" . "\\prod_{$1}^{$2}$0")
    +          ("e" . "\\exists")
    +          ("i" . "\\int_{$1}^{$2}$0")
    +          ("c" . "\\cap")
    +          ("u" . "\\cup")
    +          ("0" . "\\emptyset")))
    +
    +  )
    +
    +
    +
    +
    +
    +
  2. +
+
+ +
+

4.4.25. eglot

+
+

+After having tried out lsp-mode and lsp-bridge for a while each, I must say that eglot feels the most clean and fast to me. Rust-modes need a little extra care to get working here. +

+ +

+:CUSTOMID: h:424fbc62-84e2-42c7-a1ca-e43ea04c43e5 +

+ +
+
+(use-package eglot
+  :ensure nil
+  :hook
+  ((python-mode
+    python-ts-mode
+    c-mode
+    c-ts-mode
+    c++-mode
+    c++-ts-mode
+    tex-mode
+    LaTeX-mode
+    ) . (lambda () (progn
+                     (eglot-ensure)
+                     (add-hook 'before-save-hook 'eglot-format nil 'local))))
+  :custom
+  (eldoc-echo-area-use-multiline-p nil)
+  (completion-category-defaults nil)
+  :config
+  ;; (push '(rustic-ts-mode . eglot-rust-analyzer) eglot-server-programs)
+  (push '(rustic-mode . eglot-rust-analyzer) eglot-server-programs)
+  (add-to-list 'eglot-server-programs '((rust-mode) . (eglot-rust-analyzer "rust-analyzer")))
+  ;; (add-to-list 'eglot-server-programs '((python-mode) . ("pylsp")))
+  ;; (add-to-list 'eglot-server-programs '((c-mode) . ("clangd")))
+  :bind (:map eglot-mode-map
+              ("M-(" . flymake-goto-next-error)
+              ("C-c ," . eglot-code-actions)))
+
+(defalias 'start-lsp-server #'eglot)
+
+
+
+
+
+ +
+

4.4.26. Breadcrumb

+
+

+This simple shows the path to the current file on the top of the buffer - I just think it looks kind of neat, even though it is not extremely useful :) +

+ +
+
+(use-package breadcrumb
+  :config (breadcrumb-mode))
+
+
+
+
+
+ +
+

4.4.27. Prevent breaking of hardlinks

+
+

+This setting ensures that hard links are preserved during the backup process, which is useful for maintaining the integrity of files that are linked in multiple locations. +

+ +
+
+(setq backup-by-copying-when-linked t)
+
+
+
+
+
+ +
+

4.4.28. Dirvish

+
+

+Dirvish is an improvement upon the dired-framework and has more features like file preview etc. Sadly it has an incompatibility with openwith which is why I have disabled that package. +

+ +
+
+(use-package dirvish
+  :init
+  (dirvish-override-dired-mode)
+  :config
+  (dirvish-peek-mode)
+  (dirvish-side-follow-mode)
+  (setq dirvish-open-with-programs
+        (append dirvish-open-with-programs '(
+                                             (("xlsx" "docx" "doc" "odt" "ods") "libreoffice" "%f")
+                                             (("jpg" "jpeg" "png")              "imv" "%f")
+                                             (("pdf")                           "sioyek" "%f")
+                                             (("xopp")                          "xournalpp" "%f"))))
+  :custom
+  (delete-by-moving-to-trash t)
+  (dired-listing-switches
+   "-l --almost-all --human-readable --group-directories-first --no-group")
+  (dirvish-attributes
+   '(vc-state subtree-state nerd-icons collapse file-time file-size))
+  (dirvish-quick-access-entries
+   '(("h" "~/"              "Home")
+     ("c" "~/.dotfiles/"    "Config")
+     ("d" "~/Downloads/"    "Downloads")
+     ("D" "~/Documents/"    "Documents")
+     ("p" "~/Documents/GitHub/"  "Projects")
+     ("/" "/"               "Root")))
+  :bind
+  (("<DUMMY-i> d" . 'dirvish)
+   ("C-=" . 'dirvish-side)
+   :map dirvish-mode-map
+   ("h"   . dired-up-directory)
+   ("<left>"   . dired-up-directory)
+   ("l"   . dired-find-file)
+   ("<right>"   . dired-find-file)
+   ("j"   . evil-next-visual-line)
+   ("k"   . evil-previous-visual-line)
+   ("a"   . dirvish-quick-access)
+   ("f"   . dirvish-file-info-menu)
+   ("z"   . dirvish-history-last)
+   ("J"   . dirvish-history-jump)
+   ("y"   . dirvish-yank-menu)
+   ("/"   . dirvish-narrow)
+   ("TAB" . dirvish-subtree-toggle)
+   ("M-f" . dirvish-history-go-forward)
+   ("M-b" . dirvish-history-go-backward)
+   ("M-l" . dirvish-ls-switches-menu)
+   ("M-m" . dirvish-mark-menu)
+   ("M-t" . dirvish-layout-toggle)
+   ("M-s" . dirvish-setup-menu)
+   ("M-e" . dirvish-emerge-menu)
+   ("M-j" . dirvish-fd-jump)))
+
+
+
+
+
+
+

4.4.29. pdf-tools: pdf-viewer and support for dirvish

+
+

+This enables pdf-previewing in dirvish and gives a much better pdf-viewer than is shipped normally by emacs. +

+ +
+
+(use-package pdf-tools
+  :init
+  (if (not (boundp 'pdf-tools-directory))
+      (pdf-tools-install))
+  :mode ("\\.pdf" . pdf-view-mode))
+
+
+
+
+
+ +
+

4.4.30. Jupyter

+
+

+This is a jupyter client. Using it is a bit cumbersome though, so I have not fully explored all features. +

+ +
+
+(use-package ein)
+
+
+
+
+
+ +
+

4.4.31. undo-tree

+
+

+Base emacs undo logic is very useful, but not easy to understand for me. I prefer undo-tree, which makes switching between branches easier and also allows quickly switching back to a much older state using the visualizer. +

+ +

+Evil needs to be told to use this mode, see (evil-set-undo-system 'undo-tree) in Evil/General. +

+ +

+By default, I am not using undo-tree-mode in every buffer. This might change in the future, but for now this is fine. It can be enabled manually should the need arise. +

+ +

+While we are at it, we are also setting up a persistent undo-file for every file that we are working with. +

+ +
+
+(use-package undo-tree
+  ;; :init (global-undo-tree-mode)
+  :bind (:map undo-tree-visualizer-mode-map
+              ("h" . undo-tree-visualize-switch-branch-left)
+              ("l" . undo-tree-visualize-switch-branch-left)
+              ("j" . undo-tree-visualize-redo)
+              ("k" . undo-tree-visualize-undo))
+  :config
+  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))
+
+(add-hook 'prog-mode-hook 'undo-tree-mode)
+(add-hook 'text-mode-hook 'undo-tree-mode)
+(add-hook 'org-mode-hook 'undo-tree-mode)
+(add-hook 'latex-mode-hook 'undo-tree-mode)
+
+
+
+
+
+

4.4.32. Hydra

+
+

+Hydra allows for the writing of macro-style functions. I have not yet looked into this all too much, but it seems to be a potent feature. +

+ +
+
+(use-package hydra)
+
+
+
+
+ +
    +
  1. Text scaling
    +
    +

    +I only wrote this in order to try out hydra; rarely do I really need this. However, it can be useful for Presentations. It simply scales the text size. +

    + +
    +
    +
    +;; change the text size of the current buffer
    +(defhydra hydra-text-scale (:timeout 4)
    +  "scale text"
    +  ("j" text-scale-increase "in")
    +  ("k" text-scale-decrease "out")
    +  ("f" nil "finished" :exit t))
    +
    +
    +
    +
    +
  2. +
+
+
+

4.4.33. External Applications

+
+
+
    +
  1. Obsidian
    +
    +

    +This provides an interface to Obsidian for Emacs - as much as I want to like it, I actually enjoy using the official Obsidian app more - even though that cannot be used by Emacs directly. +

    + +

    +My workflow for Obsidian is now as follows: +

    + +
      +
    1. create notes either in Emacs or Obsidian
    2. +
    3. +look at them in the official client +

      + +

      +I hope that this package will improve, then I will come back to it one day. +

    4. +
    + +
    +
    +;; (use-package obsidian
    +;;   :ensure t
    +;;   :demand t
    +;;   :config
    +;;   (obsidian-specify-path swarsel-obsidian-vault-directory)
    +;;   (global-obsidian-mode t)
    +;;   :custom
    +;;   ;; This directory will be used for `obsidian-capture' if set.
    +;;   (obsidian-inbox-directory "Inbox")
    +;;   (bind-key (kbd "C-c M-o") 'obsidian-hydra/body 'obsidian-mode-map)
    +;;   :bind (:map obsidian-mode-map
    +;;               ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
    +;;               ("C-c C-o" . obsidian-follow-link-at-point)
    +;;               ;; Jump to backlinks
    +;;               ("C-c C-b" . obsidian-backlink-jump)
    +;;               ;; If you prefer you can use `obsidian-insert-link'
    +;;               ("C-c C-l" . obsidian-insert-wikilink)))
    +
    +
    +
    +
    +
  2. + +
  3. Anki
    +
    +

    +This section is here to make Anki usable from within Emacs - an endeavour that I have mostly given up on. +

    +
    + +
      +
    1. Basic Anki setup
      +
      +
      +
      +;; (use-package anki-editor
      +;;   :after org
      +;;   :bind (:map org-mode-map
      +;;               ("<f12>" . anki-editor-cloze-region-auto-incr)
      +;;               ("<f11>" . anki-editor-cloze-region-dont-incr)
      +;;               ("<f10>" . anki-editor-reset-cloze-number)
      +;;               ("<f9>"  . anki-editor-push-tree))
      +;;   :hook (org-capture-after-finalize . anki-editor-reset-cloze-number) ; Reset cloze-number after each capture.
      +;;   :config
      +;;   (setq anki-editor-create-decks t ;; Allow anki-editor to create a new deck if it doesn't exist
      +;;         anki-editor-org-tags-as-anki-tags t)
      +
      +;;   (defun anki-editor-cloze-region-auto-incr (&optional arg)
      +;;     "Cloze region without hint and increase card number."
      +;;     (interactive)
      +;;     (anki-editor-cloze-region swarsel-anki-editor-cloze-number "")
      +;;     (setq swarsel-anki-editor-cloze-number (1+ swarsel-anki-editor-cloze-number))
      +;;     (forward-sexp))
      +;;   (defun anki-editor-cloze-region-dont-incr (&optional arg)
      +;;     "Cloze region without hint using the previous card number."
      +;;     (interactive)
      +;;     (anki-editor-cloze-region (1- swarsel-anki-editor-cloze-number) "")
      +;;     (forward-sexp))
      +;;   (defun anki-editor-reset-cloze-number (&optional arg)
      +;;     "Reset cloze number to ARG or 1"
      +;;     (interactive)
      +;;     (setq swarsel-anki-editor-cloze-number (or arg 1)))
      +;;   (defun anki-editor-push-tree ()
      +;;     "Push all notes under a tree."
      +;;     (interactive)
      +;;     (anki-editor-push-notes '(4))
      +;;     (anki-editor-reset-cloze-number))
      +;;   ;; Initialize
      +;;   (anki-editor-reset-cloze-number)
      +;;   )
      +
      +;; (require 'anki-editor)
      +
      +
      +
      +
      +
    2. + +
    3. Own Anki functions
      +
      +
        +
      • These functions enable you to quickly set the destination note type and deck
      • +
      + +
      +
      +;; (defvar swarsel-anki-deck nil)
      +;; (defvar swarsel-anki-notetype nil)
      +;; (defvar swarsel-anki-fields nil)
      +
      +;; (defun swarsel-anki-set-deck-and-notetype ()
      +;;   (interactive)
      +;;   (setq swarsel-anki-deck  (completing-read "Choose a deck: "
      +;;                                             (sort (anki-editor-deck-names) #'string-lessp)))
      +;;   (setq swarsel-anki-notetype (completing-read "Choose a note type: "
      +;;                                                (sort (anki-editor-note-types) #'string-lessp)))
      +;;   (setq swarsel-anki-fields (progn
      +;;                               (anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,swarsel-anki-notetype)))))
      +;;   )
      +
      +;; (defun swarsel-anki-make-template-string ()
      +;;   (if (not swarsel-anki-deck)
      +;;       (call-interactively 'swarsel-anki-set-deck-and-notetype))
      +;;   (setq swarsel-temp swarsel-anki-fields)
      +;;   (concat (concat "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: " swarsel-anki-notetype "\n:ANKI_DECK: " swarsel-anki-deck "\n:END:\n** ")(pop swarsel-temp) "\n%?\n** " (mapconcat 'identity swarsel-temp "\n\n** ") "\n\n"))
      +
      +;; (defun swarsel-today()
      +;;   (format-time-string "%Y-%m-%d"))
      +
      +;; (defun swarsel-obsidian-daily ()
      +;;   (interactive)
      +;;   (if (not (file-exists-p (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
      +;;       (write-region "" nil (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))
      +;;     )
      +;;   (find-file (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
      +
      +
      +
      +
      +
    4. +
    +
  4. +
+
+ +
+

4.4.34. Email

+
+
+
    +
  1. make sure mu4e is found
    +
    +

    +This seems not to be needed - I do not yet dare to delete it though. +

    + +
    +
    +;; (let ((mu4epath
    +;;        (concat
    +;;         (f-dirname
    +;;          (file-truename
    +;;           (executable-find "mu")))
    +;;         "/../share/emacs/site-lisp/mu4e")))
    +;;   (when (and
    +;;          (string-prefix-p "/nix/store/" mu4epath)
    +;;          (file-directory-p mu4epath))
    +;;     (add-to-list 'load-path mu4epath)))
    +
    +
    +
    +
    +
  2. + +
  3. mu4e
    +
    +

    +In this section we are setting up mu4e, a mail client for emacs using mu with mbsync as backend. The mail accounts themselves are setup in the NixOS configuration, so we only need to add Emacs specific settings here. +

    + +

    +The hook functions are defined here: mu4e functions +

    + +
    +
    +(use-package mu4e
    +  :ensure nil
    +  ;; :load-path "/usr/share/emacs/site-lisp/mu4e/"
    +  ;;:defer 20 ; Wait until 20 seconds after startup
    +  :config
    +
    +  ;; This is set to 't' to avoid mail syncing issues when using mbsync
    +  (setq send-mail-function 'sendmail-send-it)
    +  (setq mu4e-change-filenames-when-moving t)
    +  (setq mu4e-mu-binary (executable-find "mu"))
    +  (setq mu4e-hide-index-messages t)
    +
    +  (setq mu4e-update-interval 180)
    +  (setq mu4e-get-mail-command "mbsync -a")
    +  (setq mu4e-maildir "~/Mail")
    +
    +  ;; enable inline images
    +  (setq mu4e-view-show-images t)
    +  ;; use imagemagick, if available
    +  (when (fboundp 'imagemagick-register-types)
    +    (imagemagick-register-types))
    +
    +  (setq mu4e-drafts-folder "/Drafts")
    +  (setq mu4e-sent-folder   "/Sent Mail")
    +  (setq mu4e-refile-folder "/All Mail")
    +  (setq mu4e-trash-folder  "/Trash")
    +
    +  (setq mu4e-maildir-shortcuts
    +        '((:maildir "/leon/Inbox"    :key ?1)
    +          (:maildir "/nautilus/Inbox" :key ?2)
    +          (:maildir "/mrswarsel/Inbox"     :key ?3)
    +          (:maildir "/Sent Mail"     :key ?s)
    +          (:maildir "/Trash"     :key ?t)
    +          (:maildir "/Drafts"     :key ?d)
    +          (:maildir "/All Mail"     :key ?a)))
    +
    +(setq user-mail-address "leon@swarsel.win"
    +      user-full-name "Leon Schwarzäugl")
    +
    +
    +(setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com)))
    +
    +
    +(add-hook 'mu4e-compose-mode-hook #'swarsel/mu4e-send-from-correct-address)
    +(add-hook 'mu4e-compose-post-hook #'swarsel/mu4e-restore-default)
    +
    +
    +
    +
  4. + +
  5. mu4e-alert
    +
    +

    +This adds the simple utility of sending desktop notifications whenever a new mail is received. I am using libnotify because I want to use this with notify-send. +

    + +
    +
    +(use-package mu4e-alert
    +:config
    +(setq mu4e-alert-set-default-style 'libnotify))
    +
    +(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
    +
    +(mu4e t)
    +
    +
    +
    +
  6. +
+
+ +
+

4.4.35. Calendar

+
+

+This provides a beautiful calender to emacs. +

+ +

+Yes, I am aware that I am exposing my university-calendar to the public here. I can imagine worse things ;) if you however know how to obscure this, let me know! +

+ +
+
+(use-package org-caldav
+  :init
+  ;; set org-caldav-sync-initalization
+  (setq swarsel-caldav-synced 0)
+  (setq org-caldav-url "https://stash.swarsel.win/remote.php/dav/calendars/Swarsele")
+  (setq org-caldav-calendars
+        '((:calendar-id "personal"
+                        :inbox "~/Calendars/leon_cal.org")))
+  ;; (setq org-caldav-backup-file "~/org-caldav/org-caldav-backup.org")
+  ;; (setq org-caldav-save-directory "~/org-caldav/")
+
+  :config
+  (setq org-icalendar-alarm-time 1)
+  ;; This makes sure to-do items as a category can show up on the calendar
+  (setq org-icalendar-include-todo t)
+  ;; This ensures all org "deadlines" show up, and show up as due dates
+  (setq org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due))
+  ;; This ensures "scheduled" org items show up, and show up as start times
+  (setq org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo))
+  )
+
+(use-package calfw
+  :ensure nil
+  :bind ("C-c A" . swarsel/open-calendar)
+  :init
+  (use-package calfw-cal
+    :ensure nil)
+  (use-package calfw-org
+    :ensure nil)
+  (use-package calfw-ical
+    :ensure nil)
+  :config
+  (bind-key "g" 'cfw:refresh-calendar-buffer cfw:calendar-mode-map)
+  (bind-key "q" 'evil-quit cfw:details-mode-map)
+  ;; (custom-set-faces
+  ;;  '(cfw:face-title ((t (:foreground "#f0dfaf" :weight bold :height 65))))
+  ;; )
+  )
+
+(defun swarsel/open-calendar ()
+  (interactive)
+  (unless (eq swarsel-caldav-synced 1) (org-caldav-sync) (setq swarsel-caldav-synced 1))
+  ;;  (select-frame (make-frame '((name . "calendar")))) ; makes a new frame and selects it
+  ;; (set-face-attribute 'default (selected-frame) :height 65) ; reduces the font size of the new frame
+  (cfw:open-calendar-buffer
+   :contents-sources
+   (list
+    (cfw:org-create-source "Purple")  ; orgmode source
+    (cfw:ical-create-source "TISS" "https://tiss.tuwien.ac.at/events/rest/calendar/personal?locale=de&token=4463bf7a-87a3-490a-b54c-99b4a65192f3" "Cyan"))))
+
+
+
+
+
+ +
+

4.4.36. Dashboard: emacs startup screen

+
+

+This sets up the dashboard, which is really quite useless. But, it looks cool and makes me happy whenever I start an emacsclient without a file name as argument :) +

+ +
+
+(use-package dashboard
+  :ensure t
+  :config
+  (dashboard-setup-startup-hook)
+  ;; (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
+  (setq dashboard-display-icons-p t ;; display icons on both GUI and terminal
+        dashboard-icon-type 'nerd-icons ;; use `nerd-icons' package
+        dashboard-set-file-icons t
+        dashboard-items '((recents . 5)
+                          (projects . 5)
+                          (agenda . 5))
+        dashboard-set-footer nil
+        dashboard-banner-logo-title "Welcome to SwarsEmacs!"
+        dashboard-image-banner-max-height 300
+        dashboard-startup-banner "~/.dotfiles/wallpaper/swarsel.png"
+        dashboard-projects-backend 'projectile
+        dashboard-projects-switch-function 'magit-status
+        dashboard-set-navigator t
+        dashboard-startupify-list '(dashboard-insert-banner
+                                    dashboard-insert-newline
+                                    dashboard-insert-banner-title
+                                    dashboard-insert-newline
+                                    dashboard-insert-navigator
+                                    dashboard-insert-newline
+                                    dashboard-insert-init-info
+                                    dashboard-insert-items
+                                    )
+        dashboard-navigator-buttons
+        `(;; line1
+          ((,""
+            "SwarselSocial"
+            "Browse Swarsele"
+            (lambda (&rest _) (browse-url "instagram.com/Swarsele")))
+
+           (,""
+            "SwarselSound"
+            "Browse SwarselSound"
+            (lambda (&rest _) (browse-url "sound.swarsel.win")) )
+           (,""
+            "SwarselSwarsel"
+            "Browse Swarsel"
+            (lambda (&rest _) (browse-url "github.com/Swarsel")) )
+           (,""
+            "SwarselStash"
+            "Browse SwarselStash"
+            (lambda (&rest _) (browse-url "stash.swarsel.win")) )
+           (,"󰫑"
+            "SwarselSport"
+            "Browse SwarselSports"
+            (lambda (&rest _) (browse-url "social.parkour.wien/@Lenno")))
+           )
+          (
+           (,"󱄅"
+            "swarsel.win"
+            "Browse swarsel.win"
+            (lambda (&rest _) (browse-url "swarsel.win")))
+           )
+          )))
+
+
+
+
+
+
+
+
+
+
+

Author: Leon Schwarzäugl

+

Created: 2024-06-20 Do 18:05

+

Validate

+
+ + diff --git a/profiles/TEMPLATE/home.nix b/profiles/TEMPLATE/home.nix index a1c5495..6d50424 100644 --- a/profiles/TEMPLATE/home.nix +++ b/profiles/TEMPLATE/home.nix @@ -126,14 +126,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; }; diff --git a/profiles/TEMPLATE/nixos.nix b/profiles/TEMPLATE/nixos.nix index c9c02fd..f1aac3e 100644 --- a/profiles/TEMPLATE/nixos.nix +++ b/profiles/TEMPLATE/nixos.nix @@ -32,7 +32,6 @@ stylix.image = ../../wallpaper/TEMPLATEwp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/common/home.nix b/profiles/common/home.nix index b45397a..6c6006f 100644 --- a/profiles/common/home.nix +++ b/profiles/common/home.nix @@ -153,7 +153,7 @@ noto-fonts noto-fonts-cjk-sans - # cura +# cura (let cura5 = appimageTools.wrapType2 rec { name = "cura5"; version = "5.4.0"; @@ -291,17 +291,6 @@ ''; }) - # (pkgs.writeShellApplication { - # name = "cdr"; - # runtimeInputs = [ pkgs.fzf ]; - # text = '' - # cdr () { - # cd "$( (find /home/swarsel/Documents/GitHub -maxdepth 1 && echo /home/swarsel/.dotfiles) | fzf )" - # } - # cdr - # ''; - # }) - (pkgs.writeShellApplication { name = "bak"; text = '' @@ -311,26 +300,15 @@ ]; - - # MIGHT NEED TO ENABLE THIS ON SURFACE!! - sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml"; sops.validateSopsFiles = false; -# sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt"; -# This will generate a new key if the key specified above does not exist -# sops.age.generateKey = true; - -# sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys"; # since we are using the home-manager implementation, we need to specify the runtime path for each secret sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";}; sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";}; sops.secrets.leon = {path = "/run/user/1000/secrets/leon";}; sops.secrets.swarselmail = {path = "/run/user/1000/secrets/swarselmail";}; sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";}; -# sops.secrets.leon = { }; -# sops.secrets.nautilus = { }; -# sops.secrets.mrswarsel = { }; programs.ssh= { enable = true; @@ -465,33 +443,6 @@ programs.ssh= { stylix.targets.emacs.enable = false; - # fonts.fontconfig.enable = true; - # gtk = { - # enable = true; - - # theme = { - # name = "Arc-Dark"; - # package = pkgs.arc-theme; - # }; - - # cursorTheme = { - # name = "capitaine-cursors"; - # package = pkgs.capitaine-cursors; - # }; - - # gtk3.extraConfig = { - # Settings = '' - # gtk-application-prefer-dark-theme=1 - # ''; - # }; - - # gtk4.extraConfig = { - # Settings = '' - # gtk-application-prefer-dark-theme=1 - # ''; - # }; - # }; - xdg.desktopEntries = { cura = { @@ -518,8 +469,6 @@ xdg.desktopEntries = { # categories = [ "Application"]; # }; - # currently unused but kept for possible future use-case - # not needed as long as schildichat is working properly element = { name = "Element Matrix Client"; genericName = "Element"; @@ -552,14 +501,8 @@ home.file = { home.sessionVariables = { EDITOR = "bash ~/.dotfiles/scripts/editor.sh"; - EDITORBAK = "bash ~/.dotfiles/scripts/editor.sh"; - # GTK_THEME = "Arc-Dark"; }; -programs.password-store = { - enable = true; - package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); -}; # zsh Integration is enabled by default for these programs.bottom.enable = true; programs.imv.enable = true; @@ -575,11 +518,18 @@ programs.nix-index.enable = true; programs.ripgrep.enable = true; programs.pandoc.enable = true; programs.fzf.enable = true; +programs.zoxide.enable = true; + +programs.password-store = { + enable = true; + package = pkgs.pass.withExtensions (exts: [exts.pass-otp]); +}; + programs.direnv = { enable = true; nix-direnv.enable = true; }; -programs.zoxide.enable = true; + programs.eza = { enable = true; icons = true; @@ -589,6 +539,7 @@ programs.eza = { "--group-directories-first" ]; }; + programs.git = { enable = true; aliases = { @@ -631,19 +582,9 @@ programs.fuzzel = { settings = { main = { layer = "overlay"; - # font = "Monospace:size=8"; lines = "10"; width = "40"; }; - colors = { - # background="293744dd"; - # text="f8f8f2ff"; - # match="8be9fdff"; - # selection-match="8be9fdff"; - # selection="44475add"; - # selection-text="f8f8f2ff"; - # border="ffd700ff"; - }; border.radius = "0"; }; }; @@ -760,7 +701,6 @@ programs.kitty = { "ctrl+shift+home" = "no_op"; "ctrl+shift+end" = "no_op"; }; - # theme = "citylights"; }; programs.zsh = { @@ -945,7 +885,7 @@ accounts.email = { programs.emacs = { enable = true; package = (pkgs.emacsWithPackagesFromUsePackage { - config = ../../Emacs.org; # tangling my Emacs.org file here instead of directly putting init.el allows avoidance of automatically installing packages in blocks using UTF-8 characters, which would break the nix evaluation happening in this line. This line is also the reason why (for now) the Emacs configuration lives in a different .org file + config = ../../programs/emacs/init.el; package = pkgs.emacs-pgtk; alwaysEnsure = true; alwaysTangle = true; @@ -1451,13 +1391,6 @@ programs.firefox = { don-t-fuck-with-paste plasma-integration - # build the rest of my firefox addons myself - # app id can be found in the manifest.json file of the .xpi - # (.xpi is just a normal archive) - # url can be found by copy url of the "add extension" button on the addon page - # the rest of the information is also found in the manifest.json, but might not be - # needed - # (let version = "3.4.5.0"; # in buildFirefoxXpiAddon { # pname = "bypass-paywalls-clean"; @@ -1475,7 +1408,6 @@ programs.firefox = { # }; # }) - (buildFirefoxXpiAddon { pname = ":emoji:"; version = "0.1.3"; @@ -1542,13 +1474,6 @@ programs.firefox = { }; }; -# programs.browserpass = { -# enable = true; -# browsers = [ -# "firefox" -# ]; -# }; - services.gnome-keyring = { enable = true; }; @@ -1557,7 +1482,6 @@ services.mbsync = { enable = true; }; - services.kdeconnect = { enable = true; indicator = true; @@ -1570,7 +1494,6 @@ services.syncthing = { }; }; -# this enables the emacs server services.emacs = { enable = true; # socketActivation.enable = false; @@ -1763,12 +1686,6 @@ wayland.windowManager.sway = { class = ".*"; }; } - { - command = "opacity 0.95"; - criteria = { - app_id = ".*"; - }; - } { command = "opacity 1"; criteria = { @@ -1790,13 +1707,13 @@ wayland.windowManager.sway = { { command = "opacity 0.8, sticky enable, border normal, move container to scratchpad"; criteria = { - title="kittyterm"; + title="^kittyterm$"; }; } { command = "opacity 0.95, sticky enable, border normal, move container to scratchpad"; criteria = { - title="spotifytui"; + title="^spotifytui$"; }; } # { diff --git a/profiles/common/nixos.nix b/profiles/common/nixos.nix index 8137983..ca31179 100644 --- a/profiles/common/nixos.nix +++ b/profiles/common/nixos.nix @@ -5,25 +5,14 @@ home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; - # login keymap - services.xserver = { - xkb.layout = "us"; - xkb.variant = "altgr-intl"; - }; +services.xserver = { + xkb.layout = "us"; + xkb.variant = "altgr-intl"; +}; + +nix.settings.experimental-features = ["nix-command" "flakes"]; - # mount NAS drive - # works only at home, but w/e - # fileSystems."/mnt/smb" = { - # device = "//192.168.1.3/Eternor"; - # fsType = "cifs"; - # options = let - # # this line prevents hanging on network split - # automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s"; - # in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"]; - # }; - - # # enable flakes - urgent line!! - nix.settings.experimental-features = ["nix-command" "flakes"]; +# use ozone for wayland - chromium apps environment.sessionVariables.NIXOS_OZONE_WL = "1"; # wordlist for look @@ -36,121 +25,107 @@ gst-libav ]); - # correct time between linux and windows - time.hardwareClockInLocalTime = true; - - # dont style GRUB with stylix - stylix.targets.grub.enable = false; # the styling makes grub more ugly - - # cura fix - # xdg.portal = { - # enable = true; - # extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; - # wlr.enable = true; - # config = { - # common = { - # default = [ - # "*" - # ]; - # }; - # }; - # }; - # wayland-related - security.polkit.enable = true; - hardware.opengl = { - enable = true; - driSupport = true; - driSupport32Bit = true; - }; +time.hardwareClockInLocalTime = true; - # systemd +# dont style GRUB with stylix +stylix.targets.grub.enable = false; # the styling makes grub more ugly + +security.polkit.enable = true; + +# systemd systemd.extraConfig = '' DefaultTimeoutStartSec=60s DefaultTimeoutStopSec=15s ''; - # audio - sound.enable = true; - # nixpkgs.config.pulseaudio = true; - hardware.pulseaudio= { - enable = true; - package = pkgs.pulseaudioFull; - }; - hardware.enableAllFirmware = true; - hardware.bluetooth.powerOnBoot = true; - hardware.bluetooth.settings = { - General = { - Enable = "Source,Sink,Media,Socket"; - }; - }; - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - networking.networkmanager.enable = true; - - time.timeZone = "Europe/Vienna"; - - i18n.defaultLocale = "en_US.UTF-8"; - i18n.extraLocaleSettings = { - LC_ADDRESS = "de_AT.UTF-8"; - LC_IDENTIFICATION = "de_AT.UTF-8"; - LC_MEASUREMENT = "de_AT.UTF-8"; - LC_MONETARY = "de_AT.UTF-8"; - LC_NAME = "de_AT.UTF-8"; - LC_NUMERIC = "de_AT.UTF-8"; - LC_PAPER = "de_AT.UTF-8"; - LC_TELEPHONE = "de_AT.UTF-8"; - LC_TIME = "de_AT.UTF-8"; +hardware.opengl = { + enable = true; + driSupport = true; + driSupport32Bit = true; +}; + +sound.enable = true; +hardware.pulseaudio= { + enable = true; + package = pkgs.pulseaudioFull; +}; + +hardware.enableAllFirmware = true; + +hardware.bluetooth.powerOnBoot = true; +hardware.bluetooth.settings = { + General = { + Enable = "Source,Sink,Media,Socket"; }; +}; + +networking.networkmanager.enable = true; + +time.timeZone = "Europe/Vienna"; + +i18n.defaultLocale = "en_US.UTF-8"; +i18n.extraLocaleSettings = { + LC_ADDRESS = "de_AT.UTF-8"; + LC_IDENTIFICATION = "de_AT.UTF-8"; + LC_MEASUREMENT = "de_AT.UTF-8"; + LC_MONETARY = "de_AT.UTF-8"; + LC_NAME = "de_AT.UTF-8"; + LC_NUMERIC = "de_AT.UTF-8"; + LC_PAPER = "de_AT.UTF-8"; + LC_TELEPHONE = "de_AT.UTF-8"; + LC_TIME = "de_AT.UTF-8"; +}; environment.systemPackages = with pkgs; [ - # yubikey packages - gnupg - yubikey-personalization - yubikey-personalization-gui - yubico-pam - # yubioath-flutter - # yubikey-manager - # yubikey-manager-qt - yubico-piv-tool - # pinentry - - # theme related - gnome.adwaita-icon-theme - - # kde-connect - xdg-desktop-portal - - # bluetooth - bluez - - # lsp-related ------------------------------- - # nix - # latex - texlab - ghostscript_headless - # wireguard - wireguard-tools - # rust - rust-analyzer - clippy - rustfmt - # cpp - clang-tools - # + cuda - cudatoolkit - #lsp-bridge / python - gcc - gdb - (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) - # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) - # -------------------------------------------- - -(stdenv.mkDerivation { + # yubikey packages + gnupg + yubikey-personalization + yubikey-personalization-gui + yubico-pam + # yubioath-flutter + # yubikey-manager + # yubikey-manager-qt + yubico-piv-tool + # pinentry + + # theme related + gnome.adwaita-icon-theme + + # kde-connect + xdg-desktop-portal + + # bluetooth + bluez + + # lsp-related ------------------------------- + # nix + # latex + texlab + ghostscript_headless + # wireguard + wireguard-tools + # rust + rust-analyzer + clippy + rustfmt + # cpp + clang-tools + # + cuda + cudatoolkit + #lsp-bridge / python + gcc + gdb + (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) + # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server])) + # -------------------------------------------- + + (stdenv.mkDerivation { name = "oama"; src = pkgs.fetchurl { - name = "oama"; - url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz"; - sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc="; + name = "oama"; + url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz"; + sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc="; }; phases = [ @@ -163,12 +138,9 @@ environment.systemPackages = with pkgs; [ mv $out/oama-0.13.1-Linux-x86_64-static/oama $out/bin/ ''; -}) - + }) - - - ]; +]; programs.dconf.enable = true; programs.evince.enable = true; @@ -184,13 +156,13 @@ environment.pathsToLink = [ "/share/zsh" ]; services.blueman.enable = true; - # enable scanners over network - hardware.sane = { - enable = true; - extraBackends = [ pkgs.sane-airscan ]; - }; +# enable scanners over network +hardware.sane = { + enable = true; + extraBackends = [ pkgs.sane-airscan ]; +}; - # enable discovery and usage of network devices (esp. printers) +# enable discovery and usage of network devices (esp. printers) services.printing.enable = true; services.printing.drivers = [ pkgs.gutenprint @@ -210,33 +182,32 @@ BrowseProtocols all openFirewall = true; }; - # nautilus file manager - services.gvfs.enable = true; +services.gvfs.enable = true; - # Make CAPS work as a dual function ESC/CTRL key - services.interception-tools = { - enable = true; - udevmonConfig = let - dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" '' - TIMING: - TAP_MILLISEC: 200 - DOUBLE_TAP_MILLISEC: 0 - - MAPPINGS: - - KEY: KEY_CAPSLOCK - TAP: KEY_ESC - HOLD: KEY_LEFTCTRL - ''; - in '' - - JOB: | - ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \ - | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \ - | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE - DEVICE: - EVENTS: - EV_KEY: [KEY_CAPSLOCK] +# Make CAPS work as a dual function ESC/CTRL key +services.interception-tools = { + enable = true; + udevmonConfig = let + dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" '' + TIMING: + TAP_MILLISEC: 200 + DOUBLE_TAP_MILLISEC: 0 + + MAPPINGS: + - KEY: KEY_CAPSLOCK + TAP: KEY_ESC + HOLD: KEY_LEFTCTRL ''; - }; + in '' + - JOB: | + ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \ + | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \ + | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE + DEVICE: + EVENTS: + EV_KEY: [KEY_CAPSLOCK] + ''; +}; programs.ssh.startAgent = false; diff --git a/profiles/fourside/home.nix b/profiles/fourside/home.nix index 9b2cb3a..a560a24 100644 --- a/profiles/fourside/home.nix +++ b/profiles/fourside/home.nix @@ -102,14 +102,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; }; diff --git a/profiles/fourside/nixos.nix b/profiles/fourside/nixos.nix index 49f7cb2..32dcffa 100644 --- a/profiles/fourside/nixos.nix +++ b/profiles/fourside/nixos.nix @@ -55,7 +55,6 @@ stylix.image = ../../wallpaper/lenovowp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/onett/home.nix b/profiles/onett/home.nix index bd0e950..ce98816 100644 --- a/profiles/onett/home.nix +++ b/profiles/onett/home.nix @@ -115,14 +115,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; }; diff --git a/profiles/onett/nixos.nix b/profiles/onett/nixos.nix index 0b9c390..e1632bb 100644 --- a/profiles/onett/nixos.nix +++ b/profiles/onett/nixos.nix @@ -34,7 +34,6 @@ stylix.image = ../../wallpaper/lenovowp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/stand/home.nix b/profiles/stand/home.nix index a5194a0..6e665e3 100644 --- a/profiles/stand/home.nix +++ b/profiles/stand/home.nix @@ -82,14 +82,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; }; diff --git a/profiles/stand/nixos.nix b/profiles/stand/nixos.nix index 765f876..68e1bb3 100644 --- a/profiles/stand/nixos.nix +++ b/profiles/stand/nixos.nix @@ -16,7 +16,6 @@ stylix.image = ../../wallpaper/standwp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/surface/home.nix b/profiles/surface/home.nix index 139cfff..123bd3a 100644 --- a/profiles/surface/home.nix +++ b/profiles/surface/home.nix @@ -9,7 +9,6 @@ stylix.image = ../../wallpaper/surfacewp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/threed/home.nix b/profiles/threed/home.nix index f65aa24..d5bf174 100644 --- a/profiles/threed/home.nix +++ b/profiles/threed/home.nix @@ -89,14 +89,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; diff --git a/profiles/threed/nixos.nix b/profiles/threed/nixos.nix index dfc2656..c88965d 100644 --- a/profiles/threed/nixos.nix +++ b/profiles/threed/nixos.nix @@ -36,7 +36,6 @@ stylix.image = ../../wallpaper/surfacewp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/profiles/twoson/home.nix b/profiles/twoson/home.nix index 9f21c26..a9cb3ef 100644 --- a/profiles/twoson/home.nix +++ b/profiles/twoson/home.nix @@ -115,14 +115,11 @@ startup = [ { command = "nextcloud --background";} - # { command = "spotify";} { command = "discord --start-minimized";} - # { command = "element-desktop --hidden";} { command = "element-desktop --hidden -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";} { command = "ANKI_WAYLAND=1 anki";} { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";} { command = "nm-applet";} - # { command = "sleep 60 && syncthingtray --wait"; } ]; }; diff --git a/profiles/twoson/nixos.nix b/profiles/twoson/nixos.nix index 9218afe..aa72ed6 100644 --- a/profiles/twoson/nixos.nix +++ b/profiles/twoson/nixos.nix @@ -32,7 +32,6 @@ stylix.image = ../../wallpaper/t14swp.png; - stylix = { base16Scheme = ../../wallpaper/swarsel.yaml; # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml"; diff --git a/programs/emacs/init.el b/programs/emacs/init.el index 2877974..a085531 100644 --- a/programs/emacs/init.el +++ b/programs/emacs/init.el @@ -172,6 +172,12 @@ create a new one." (visual-fill-column-mode 1)) (defun swarsel/org-babel-tangle-config () + (when (string-equal (buffer-file-name) + swarsel-swarsel-org-filepath) + ;; Dynamic scoping to the rescue + (let ((org-confirm-babel-evaluate nil)) + (org-html-export-to-html) + (org-babel-tangle))) (when (string-equal (buffer-file-name) swarsel-emacs-org-filepath) ;; Dynamic scoping to the rescue @@ -280,11 +286,13 @@ create a new one." ;; "cj" '((lambda () (interactive) (org-capture nil "jj")) :which-key "journal") ;; "cs" '(markdown-download-screenshot :which-key "screenshot") "l" '(:ignore l :which-key "links") - "le" '((lambda () (interactive) (find-file swarsel-emacs-org-filepath)) :which-key "Emacs.org") + "lc" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (org-overview) )) :which-key "SwarselSystems.org") + "le" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "Emacs") ) (org-overview) (org-cycle) )) :which-key "Emacs.org") + "ln" '((lambda () (interactive) (progn (find-file swarsel-swarsel-org-filepath) (goto-char (org-find-exact-headline-in-buffer "System") ) (org-overview) (org-cycle))) :which-key "Nixos.org") "ls" '((lambda () (interactive) (find-file "/smb:Swarsel@192.168.1.3:")) :which-key "Server") "lo" '(dired swarsel-obsidian-vault-directory :which-key "obsidian") ;; "la" '((lambda () (interactive) (find-file swarsel-org-anki-filepath)) :which-key "anki") - "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org") + ;; "ln" '((lambda () (interactive) (find-file swarsel-nix-org-filepath)) :which-key "Nix.org") "lp" '((lambda () (interactive) (projectile-switch-project)) :which-key "switch project") "lg" '((lambda () (interactive) (magit-list-repositories)) :which-key "list git repos") ;; "a" '(:ignore a :which-key "anki") @@ -343,7 +351,9 @@ create a new one." swarsel-projects-directory "~/Documents/GitHub") (setq swarsel-emacs-org-filepath (expand-file-name "Emacs.org" swarsel-dotfiles-directory) - swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory)) + swarsel-nix-org-filepath (expand-file-name "Nix.org" swarsel-dotfiles-directory) + swarsel-swarsel-org-filepath (expand-file-name "SwarselSystems.org" swarsel-dotfiles-directory) + ) ;; set Emacs main configuration .org names @@ -368,11 +378,6 @@ create a new one." swarsel-org-tasks-filepath (expand-file-name swarsel-tasks-org-file swarsel-org-directory) swarsel-org-archive-filepath (expand-file-name swarsel-archive-org-file swarsel-org-directory)) -;; set paths to authentication files (forge) -;; (setq auth-source-pass-filename "~/.local/share/password-store" -(setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") - auth-source-cache-expiry nil) ; default is 2h - ;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d (setq user-emacs-directory (expand-file-name "~/.cache/emacs/") url-history-file (expand-file-name "url/history" user-emacs-directory)) @@ -740,6 +745,9 @@ create a new one." (setq-default indicate-buffer-boundaries t) +(setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") + auth-source-cache-expiry nil) ; default is 2h + (use-package org ;;:diminish (org-indent-mode) :hook (org-mode . swarsel/org-mode-setup) @@ -873,6 +881,7 @@ create a new one." 'org-babel-load-languages '((emacs-lisp . t) (python . t) + (js . t) (shell . t) ))