From bc8c52554de786e75e4f074e59b857c24265ecde Mon Sep 17 00:00:00 2001 From: Swarsel Date: Thu, 20 Jun 2024 01:43:49 +0200 Subject: [PATCH] refactor: revamp whole Emacs.org This is the first part of consolidating my Emacs and NixOS config into a single file. This commit refactors the whole of Emacs.org and adds more bits of prose to the configuration sections. --- Emacs.html | 4076 ++++++++++++++++++++++++++++++++++ Emacs.org | 3710 +++++++++++++++++-------------- Nix.org | 18 + profiles/common/home.nix | 18 + programs/emacs/custom.el | 24 +- programs/emacs/early-init.el | 81 +- programs/emacs/init.el | 2070 +++++++---------- programs/git/.gitmessage | 10 + 8 files changed, 7068 insertions(+), 2939 deletions(-) create mode 100644 Emacs.html create mode 100644 programs/git/.gitmessage diff --git a/Emacs.html b/Emacs.html new file mode 100644 index 0000000..0a689c1 --- /dev/null +++ b/Emacs.html @@ -0,0 +1,4076 @@ + + + + + + + +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 index e240b87..53a4d07 100644 --- a/Emacs.org +++ b/Emacs.org @@ -1,41 +1,733 @@ #+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))) -** TODO Agenda for this file [1/4] -- [ ] Content [0/2] - - [ ] Structure this file in a readable manner - - [ ] Add commentary to major sections -- [-] Language [1/2] - - [X] English across the entire file - - [ ] Stay in first person -- [X] Formatting [3/3] - - [X] One free line at start and end of each source block - - [X] Uniform indentation across file - - [X] One free line at the start and end of each header -- [ ] Finish all TODO blocks in this file +*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: -* Initialization -** Startup performance +This is my Emacs literate configuration. It is part of a NixOS system that is fully declarative and can be found here: -#+begin_src emacs-lisp +- [[https:github.com/Swarsel/.dotfiles][~SwarselSystems~ on github.com]] +- [[https:swagit.swarsel.win/Swarsel/.dotfiles][~SwarselSystems~ on swagit.swarsel.win]] - ;; The default is 800 kilobytes. Measured in bytes. +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 -** Directory setup +#+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 - ;; use UTF-8 everywhere - (set-language-environment "UTF-8") + (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)))) - ;; 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") + (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" @@ -52,7 +744,7 @@ swarsel-anki-org-file "Anki.org" swarsel-tasks-org-file "Tasks.org" swarsel-archive-org-file "Archive.org" - swarsel-org-folder-name "org" + swarsel-org-folder-name "Org" swarsel-obsidian-daily-folder-name "⭐ Personal/Journal" swarsel-obsidian-folder-name "Obsidian" swarsel-obsidian-vault-name "Main") @@ -74,19 +766,18 @@ (setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") auth-source-cache-expiry nil) ; default is 2h - ;; set pandoc for markdown compilation - (setq markdown-command "pandoc") - ;; set org-caldav-sync-initalization - (setq swarsel-caldav-synced 0) #+end_src -** Package System Setup +** Unclutter .emacs.d +:PROPERTIES: +:CUSTOM_ID: h:0cf30b76-91d9-41da-a10b-74199bc36d40 +:END: -This is handles by nix, no configuration is needed here. Do note that package.el must stay enabled in early-init.el. +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. -** Keeping .emacs.d clean +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 @@ -96,283 +787,17 @@ This is handles by nix, no configuration is needed here. Do note that package.el ;; Use no-littering to automatically set common paths to the new user-emacs-directory (use-package no-littering) - (setq custom-file (expand-file-name "programs/emacs/custom.el" swarsel-dotfiles-directory)) + (setq custom-file (make-temp-file "emacs-custom-")) (load custom-file t) #+end_src -* Behavior -** General - -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 - - (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 - (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) - ;; less noise when compiling elisp - (setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) - (setq native-comp-async-report-warnings-errors nil) - (setq load-prefer-newer t) - - (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')"))) - -#+end_src - -** Disable GUI distractions - -These settings are mostly useless in my eyes and provide little more than a distraction. - -#+begin_src emacs-lisp - - ;; (scroll-bar-mode -1) - ;; (tool-bar-mode -1) - ;; (tooltip-mode -1) - ;; (menu-bar-mode -1) - (setq visible-bell nil) - (setq initial-major-mode 'fundamental-mode - initial-scratch-message nil) - -#+end_src - -** Indentation - -#+begin_src emacs-lisp - - (setq-default indent-tabs-mode nil - tab-width 2) - - (setq tab-always-indent 'complete) - ;; dont send nag when creating python files - (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-method 'character) - ;; (setq highlight-indent-guides-character ?|) - (setq highlight-indent-guides-responsive 'top) - ) - ;;(set-face--background 'highlight-indent-guides-odd-face "dark slate gray") - ;;(set-face-background 'highlight-indent-guides-even-face "steel blue") - ;;(set-face-foreground 'highlight-indent-guides-character-face "dark violet") -#+end_src - -** Scrolling - -By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying. - -#+begin_src emacs-lisp - - ;; (setq scroll-step 1 - ;; scroll-margin 4 - ;; scroll-conservatively 101) - - (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) - ;; (setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time - ;; (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling - ;; (setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse - - (pixel-scroll-precision-mode 1) - - ;; (use-package fast-scroll - ;; :ensure nil - ;; :init (fast-scroll-mode 1)) - -#+end_src - -** 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. - -#+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 - -** Evil -#+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 - ; sane splitting - (setq evil-split-window-below t) - (setq evil-vsplit-window-right t) - :config - (evil-mode 1) - (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state) ; alternative for exiting insert mode - (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join) ; dont show help but instead do normal vim delete backwards - (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 undo system - (evil-set-undo-system 'undo-tree) - ;; Use visual line motions even outside of visual-line-mode buffers - ;; (evil-global-set-key 'motion "j" 'evil-next-visual-line) - ;; (evil-global-set-key 'motion "k" 'evil-previous-visual-line) - - ;; 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 - - ; require a specific evil state - (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))) - - ;; Evil configuration for different modes - (use-package evil-collection - :after evil - :config - (evil-collection-init) - (setq forge-add-default-bindings nil)) - - ;; 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)) - - ;; for parentheses-heavy languades modify evil commands to keep balance of parantheses - (use-package evil-cleverparens) - - ;; enables surrounding text with S - (use-package evil-surround - :config - (global-evil-surround-mode 1)) - - - -#+end_src - -** ispell - -#+begin_src emacs-lisp - - ;; set the NixOS wordlist by hand - (setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST") - -#+end_src - -** Inhibit Messages in Echo Area - -#+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 - -** undo-tree - -Base emacs undo logic is very useful, but not easy to understand. I prefer undo-tree, which makes switching between branches easier and also allows quickly switching back to a much older state using the visualizer - -#+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 - ** 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 @@ -394,226 +819,360 @@ Base emacs undo logic is very useful, but not easy to understand. I prefer undo- 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: -** Move up one directory for find-file +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. -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. +** 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 - (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)))) + ;; use UTF-8 everywhere + (set-language-environment "UTF-8") - (define-key minibuffer-local-filename-completion-map - [C-backspace] #'up-directory) + ;; 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") -#+end_src + ;; (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) -* Custom Keybindings -#+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 - "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") - ;; "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" 'duplicate-line ; duplicate line on CURSOR - "C-M-j" 'consult-buffer - "C-s" 'consult-line - "M-o" 'avy-goto-char-timer - "C-" 'my-python-shell-run - ) - -(defun swarsel/toggle-evil-state () - (interactive) - (if (or (evil-emacs-state-p) (evil-insert-state-p)) - (evil-normal-state) - (evil-emacs-state))) - -#+end_src - -* UI -** General - -- This sets up some basic UI elements - -#+begin_src emacs-lisp - - (setq inhibit-startup-message t) - - ;; (set-fringe-mode nil) ; Give some breathing room - - ;; Increase undo limit and allow for more fine grained undo, base emacs deletes way too much on undo (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')"))) - ;; (display-time-mode 1) - (global-subword-mode 1) ; Iterate through CamelCase words - - (use-package rainbow-mode - :config (rainbow-mode)) - - + (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 -** Looks -*** Font Configuration +** Mark all themes as safe +:PROPERTIES: +:CUSTOM_ID: h:0debe8fd-b319-4ab7-a92c-784fa7896b75 +:END: -- You have to install these fonts manually on Windows: https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip -- On linux (fedora): 'sudo dnf install fira-code-fonts' +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 - ;; (defun swarsel/font-setup (frame) - ;; (set-face-attribute 'default nil :font swarsel-standard-font :height swarsel/default-font-size) - ;; ;; Set the fixed pitch face - basically normal text - ;; (set-face-attribute 'fixed-pitch nil :font swarsel-standard-font :height 140) - ;; ;; Set the variable pitch face - basically headers etc - ;; (set-face-attribute 'variable-pitch nil :font swarsel-alt-font :height 100 :weight 'regular) - ;; (set-face-attribute 'region nil :foreground "cyan" :background "gray40" :weight 'bold) - ;; ;; (remove-hook 'after-make-frame-functions #'swarsel/font-setup) - ;; ) + (setq custom-safe-themes t) - ;; (add-hook 'after-make-frame-functions #'swarsel/font-setup) +#+end_src +** Show less compilation warnings +:PROPERTIES: +:CUSTOM_ID: h:b587e869-9911-443b-bc6d-8fb3ce31908d +:END: - ;; (defun swarsel/font-setup (frame) - (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")) +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). - (set-face-attribute 'default nil :height 100) - (set-face-attribute 'fixed-pitch nil :height 1.0) +#+begin_src emacs-lisp - (set-face-attribute 'variable-pitch nil - :family "IBM Plex Sans" - :weight 'regular - :height 1.06) - ;; (enable-theme 'doom-city-lights) - ;; ) + (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 - ;; (add-hook 'after-make-frame-functions #'swarsel/font-setup) #+end_src -*** Color Theme +** 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 - :defer t - :custom (solaire-global-mode +1)) + :custom + (solaire-global-mode +1)) (use-package doom-themes - :defer t - :hook (server-after-make-frame . (lambda () (load-theme - 'doom-city-lights t))) - ) + :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 -*** Transparent background +** Icons +:PROPERTIES: +:CUSTOM_ID: h:eb0ea526-a83a-4664-b3a1-2b40d3a31493 +:END: -This is handled by Nix and no longer needed here. +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 - ;; (set-frame-parameter (selected-frame) 'alpha '(95 . 95)) - ;; (add-to-list 'default-frame-alist '(alpha . (95 . 95))) - ;; (set-frame-parameter (selected-frame) 'fullscreen 'maximized) - ;; (add-to-list 'default-frame-alist '(fullscreen . maximized)) + (use-package nerd-icons) #+end_src -*** Variable Pitch Mode +** 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 @@ -628,33 +1187,49 @@ This is handled by Nix and no longer needed here. #+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. +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 nerd-icons) - - ;; Adds a more beautiful modeline with less clutter (use-package doom-modeline - :init (doom-modeline-mode) + :init + (doom-modeline-mode) + (column-number-mode) :custom ((doom-modeline-height 22) (doom-modeline-indent-info nil) (doom-modeline-buffer-encoding nil))) - ;; Generally show line numbers - (column-number-mode) - - ;; (unless (string-match-p "^Power N/A" (battery)) ; On laptops... - ;; (display-battery-mode 1)) #+end_src -** Helper Modes (Ivy..., Vertico..., which-key, helpful) -*** Vertico and friends +** 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: -Soon I want to try out this new hot stuff - just at the moment there is too much other stuff to do. +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 @@ -671,128 +1246,153 @@ Soon I want to try out this new hot stuff - just at the moment there is too much :init (vertico-mode) (vertico-mouse-mode)) +#+end_src - (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)) +**** vertico-directory +:PROPERTIES: +:CUSTOM_ID: h:10d4f2bd-8c72-430b-a9ed-9b5e279ec0b4 +:END: - (use-package orderless - :custom - (completion-styles '(orderless flex basic)) - (completion-category-overrides '((file (styles . (partial-completion))) - (eglot (styles orderless))))) - - (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) - ("M-g M-g" . consult-goto-line) - ("M-g i" . consult-imenu) - ("M-s s" . consult-line) - ("M-s M-s" . consult-line-multi) - :map minibuffer-local-map - ("C-j" . next-line) - ("C-k" . previous-line))) - - (use-package embark - :bind - (("C-." . embark-act) - ("M-." . embark-dwim) - ("C-h B" . embark-bindings)) - :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))))) - - (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)) - - (use-package marginalia - :after vertico - :init - (marginalia-mode) - (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) - - (use-package nerd-icons-completion - :after (marginalia nerd-icons) - :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) - :init - (nerd-icons-completion-mode)) +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 -*** Ivy + Counsel +**** 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 ivy - ;; :init (ivy-mode 1) - ;; :diminish - ;; :bind (("C-s" . swiper) ; call swiper (find tool) - ;; :map ivy-minibuffer-map - ;; ;("TAB" . ivy-alt-done) ; autocomplete - ;; ("C-l" . ivy-alt-done) - ;; ("C-" . ivy-alt-done) ; for kyria - ;; ("C-h" . counsel-up-directory) ; for kyria - ;; ("C-" . counsel-up-directory) ; for kyria - ;; ("C-j" . ivy-next-line) ; go up and down in ivy using vim keys - ;; ("C-" . ivy-next-line) ; for kyria - ;; ("C-k" . ivy-previous-line) - ;; ("C-" . ivy-previous-line) ; for kyria - ;; :map ivy-switch-buffer-map - ;; ("C-k" . ivy-previous-line) - ;; ("C-" . ivy-previous-line) ; for kyria - ;; ("C-l" . ivy-done) - ;; ("C-" . ivy-done) ; for kyria - ;; ("C-d" . ivy-switch-buffer-kill) - ;; :map ivy-reverse-i-search-map - ;; ("C-k" . ivy-previous-line) - ;; ("C-" . ivy-previous-line) ; for kyria - ;; ("C-d" . ivy-reverse-i-search-kill)) - ;; :config - ;; (setq ivy-use-virtual-buffers t) - ;; (setq ivy-count-format "(%d/%d) ") - ;; (setq ivy-wrap t)) + (use-package orderless + :custom + (completion-styles '(orderless flex basic)) + (completion-category-overrides '((file (styles . (partial-completion))) + (eglot (styles orderless))))) - ;; ;; More information about functions in ivy-mode - ;; (use-package ivy-rich - ;; :init - ;; (ivy-rich-mode 1)) +#+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)) - ;; (use-package counsel - ;; :init (counsel-mode 1) - ;; :bind (("C-M-j" . counsel-switch-buffer) - ;; ("M-x" . counsel-M-x) - ;; ("C-x b" . counsel-ibuffer) - ;; ("C-x C-f" . counsel-find-file) - ;; :map minibuffer-local-map - ;; ("C-r" . 'counsel-minibuffer-history)) - ;; :config - ;; (setq ivy-initial-inputs-alist nil)) #+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 @@ -802,16 +1402,6 @@ Soon I want to try out this new hot stuff - just at the moment there is too much :config (setq which-key-idle-delay 0.3)) - ;; (use-package helpful - ;; :custom - ;; (counsel-describe-function-function #'helpful-callable) - ;; (counsel-describe-variable-function #'helpful-variable) - ;; :bind - ;; ([remap describe-function] . counsel-describe-function) - ;; ([remap describe-command] . helpful-command) - ;; ([remap describe-variable] . counsel-describe-variable) - ;; ([remap describe-key] . helpful-key)) - (use-package helpful :bind (("C-h f" . helpful-callable) @@ -822,83 +1412,86 @@ Soon I want to try out this new hot stuff - just at the moment there is too much (setq help-window-select nil)) #+end_src -** Text Scaling -#+begin_src emacs-lisp - - (use-package hydra) - - ;; 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 - ** 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 - :config - (ligature-set-ligatures 'prog-mode - '("|||>" "<|||" "<==>" "" "---" "-<<" - "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" - "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "" "###" "#_(" "..<" - "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~=" - "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|" - "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" - ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:" - "<$" "<=" "<>" "<-" "<<" "<+" "" "++" "?:" "?=" - "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\" - "://" ";;")) - (global-ligature-mode t)) + (use-package ligature + :init + (global-ligature-mode t) + :config + (ligature-set-ligatures 'prog-mode + '("|||>" "<|||" "<==>" "" "---" "-<<" + "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" + "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "" "###" "#_(" "..<" + "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~=" + "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|" + "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" + ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:" + "<$" "<=" "<>" "<-" "<<" "<+" "" "++" "?:" "?=" + "?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\" + "://" ";;"))) #+end_src -** Popup + Shackle Buffers +** 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)) + :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)) + :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 @@ -906,143 +1499,158 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -* Org Mode -** General +* 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 - (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)) - ;; (setq evil-auto-indent nil) - ;;(diminish org-indent-mode) + (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) - ;; (defun swarsel/org-font-setup () - ;; ;; Replace list hyphen with dot - ;; (font-lock-add-keywords 'org-mode - ;; '(("^ *\\([-]\\) " - ;; (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))) + ;; (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" "$" "$$" "\\(" "\\["))) - (use-package org - ;;:diminish (org-indent-mode) - :hook (org-mode . swarsel/org-mode-setup) - :bind ("C-" . org-fold-outer) - :config - (setq org-ellipsis " ⤵" - 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: - ;; (setq org-agenda-files - ;; '(swarsel-org-tasks-filepath - ;; swarsel-org-archive-filepath - ;; swarsel-org-anki-filepath)) - (setq org-agenda-files '("/home/swarsel/Calendars/leon_cal.org")) +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@)"))) - ;; (require 'org-habit) - ;; (add-to-list 'org-modules 'org-habit) - ;; (setq org-habit-graph-column 60) + ;; 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"))))) - ;; (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@)"))) + ("n" "Next Tasks" + ((todo "NEXT" + ((org-agenda-overriding-header "Next Tasks"))))) - ;; (setq org-refile-targets - ;; '((swarsel-archive-org-file :maxlevel . 1) - ;; (swarsel-anki-org-file :maxlevel . 1) - ;; (swarsel-tasks-org-file :maxlevel . 1))) + ("W" "Work Tasks" tags-todo "+work-email") - ;; ;; 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" "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))))))) - ;; ("W" "Work Tasks" tags-todo "+work-email") - ;; ;; Low-effort next actions - ;; ("e" tags-todo "+TODO=\"NEXT\"+Effort<15&+Effort>0" - ;; ((org-agenda-overriding-header "Low Effort Tasks") - ;; (org-agenda-max-todos 20) - ;; (org-agenda-files org-agenda-files))) +#+end_src +*** org capture templates +:PROPERTIES: +:CUSTOM_ID: h:23183635-3d46-4d7d-8eda-e0a085b335ef +:END: - ;; ("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))))))) +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)) +#+begin_src emacs-lisp - ;; ("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) + (setq org-capture-templates + `( + ("a" "Anki basic" + entry + (file+headline swarsel-org-anki-filepath "Dispatch") + (function swarsel-anki-make-template-string)) - ;; ("j" "Journal Entries") - ;; ("jj" "Journal" entry - ;; (file+olp+datetree swarsel-org-journal-filepath) - ;; "\n* %<%I:%M %p> - Journal :journal:\n\n%?\n\n" - ;; ;; ,(dw/read-file-as-string "~/Notes/Templates/Daily.org") - ;; :clock-in :clock-resume - ;; :empty-lines 1))) - - ;; (swarsel/org-font-setup) - ) + ("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 +*** 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 @@ -1071,47 +1679,47 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** org-appear +*** 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 - ;; Show hidden emphasis markers - ;; (use-package org-appear - ;; :hook (org-mode . org-appear-mode) - ;; :init - ;; (setq org-appear-autolinks t) - ;; (setq org-appear-autosubmarkers t) - ;; ) + (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 -** Heading Bullets +*** 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 org-bullets - ;; :after org - ;; :hook (org-mode . org-bullets-mode) - ;; :custom - ;; (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●"))) - -#+end_src - -** 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)) - (use-package visual-fill-column :hook (org-mode . swarsel/org-mode-visual-fill)) #+end_src -** Fix headings not folding sometimes +*** 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 @@ -1119,10 +1727,21 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** Babel -*** Language Configuration +*** Babel +:PROPERTIES: +:CUSTOM_ID: h:3e0b6da3-0497-4080-bb49-bab949c03bc4 +:END: -- This configures the languages that babel recognizes +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 @@ -1137,10 +1756,15 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -*** old easy structure templates +**** 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) @@ -1151,29 +1775,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** Auto-tangle Configuration Files +*** aucTex +:PROPERTIES: +:CUSTOM_ID: h:4696e2fc-3296-47dc-8fc3-66912c329d4c +:END: -- Automatically tangles all configuration blocks in this file to the defined Emacs orgfile - -#+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-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)))) - - (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'swarsel/org-babel-tangle-config))) - -#+end_src - -** aucTex +This provides several utilities for LaTeX in Emacs, including many completions and convenience functions for math-mode. #+begin_src emacs-lisp @@ -1194,7 +1801,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** org-download +*** 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 @@ -1215,7 +1827,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** org-fragtog +*** 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 @@ -1225,21 +1842,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** Fold current heading +*** org-modern +:PROPERTIES: +:CUSTOM_ID: h:95b42e77-767c-4461-9ba8-b1c1cd18266c +:END: -#+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 - -** 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. #+begin_src emacs-lisp @@ -1251,7 +1859,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -** Presentations +*** 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 @@ -1358,8 +1971,12 @@ Soon I want to try out this new hot stuff - just at the moment there is too much (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: -* 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. #+begin_src emacs-lisp @@ -1368,141 +1985,32 @@ Soon I want to try out this new hot stuff - just at the moment there is too much #+end_src -* Markdown Mode -** Mode +** 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 -(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))) + (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 -** org-download port - -This is a section adapted from org-download to make some org-download functions available in markdown mode. I need this because I am still using Obsidian to manage my notes, and that used markdown files. - -#+begin_src emacs-lisp - - ;; https://github.com/mooreryan/markdown-dnd-images - ;; (add-to-list 'load-path "~/.emacs.d/packages") - ;; (require 'markdown-dnd-images) - ;; (setq dnd-save-directory "images") - - ;; (setq dnd-save-buffer-name nil) - - ;; (setq dnd-view-inline t) - - ;; (setq dnd-capture-source nil) - - ;; these next lines provide an interface for org-download in markdown mode for use with obsidian - - ;; (defvar org-download-markdown-link-format - ;; "![[./%s]]\n" - ;; "Format of the file link to insert.") - - ;; (defcustom org-download-markdown-link-format-function #'org-download-markdown-link-format-function-default - ;; "Function that takes FILENAME and returns a org link." - ;; :type 'function) - - ;; (defun org-download-markdown-link-format-function-default (filename) - ;; "The default function of `org-download-link-format-function'." - ;; (if (and (>= (string-to-number org-version) 9.3) - ;; (eq org-download-method 'attach)) - ;; (format "[[attachment:%s]]\n" - ;; (org-link-escape - ;; (file-relative-name filename (org-attach-dir)))) - ;; (format org-download-markdown-link-format - ;; (org-link-escape - ;; (funcall org-download-abbreviate-filename-function filename))))) - - ;; (defun org-download-markdown-image (link) - ;; "Save image at address LINK to `org-download--dir'." - ;; (interactive "sUrl: ") - ;; (let* ((link-and-ext (org-download--parse-link link)) - ;; (filename - ;; (cond ((and (derived-mode-p 'org-mode) - ;; (eq org-download-method 'attach)) - ;; (let ((org-download-image-dir (org-attach-dir t)) - ;; org-download-heading-lvl) - ;; (apply #'org-download--fullname link-and-ext))) - ;; ((fboundp org-download-method) - ;; (funcall org-download-method link)) - ;; (t - ;; (apply #'org-download--fullname link-and-ext))))) - ;; (setq org-download-path-last-file filename) - ;; (org-download--image link filename) - ;; (when (org-download-org-mode-p) - ;; (when (eq org-download-method 'attach) - ;; (org-attach-attach filename nil 'none)) - ;; (org-download-markdown-insert-link link filename)) - ;; (when (and (eq org-download-delete-image-after-download t) - ;; (not (url-handler-file-remote-p (current-kill 0)))) - ;; (delete-file link delete-by-moving-to-trash)))) - - ;; (defun org-download-markdown-screenshot (&optional basename) - ;; "Capture screenshot and insert the resulting file. - ;; The screenshot tool is determined by `org-download-screenshot-method'." - ;; (interactive) - ;; (let* ((screenshot-dir (file-name-directory org-download-screenshot-file)) - ;; (org-download-screenshot-file - ;; (if basename - ;; (concat screenshot-dir basename) org-download-screenshot-file))) - ;; (make-directory screenshot-dir t) - ;; (if (functionp org-download-screenshot-method) - ;; (funcall org-download-screenshot-method - ;; org-download-screenshot-file) - ;; (shell-command-to-string - ;; (format org-download-screenshot-method - ;; org-download-screenshot-file))) - ;; (when (file-exists-p org-download-screenshot-file) - ;; (org-download-markdown-image org-download-screenshot-file) - ;; (delete-file org-download-screenshot-file)))) - - - ;; (defun org-download-markdown-insert-link (link filename) - ;; (let* ((beg (point)) - ;; (line-beg (line-beginning-position)) - ;; (indent (- beg line-beg)) - ;; (in-item-p (org-in-item-p)) - ;; str) - ;; (if (looking-back "^[ \t]+" line-beg) - ;; (delete-region (match-beginning 0) (match-end 0)) - ;; (newline)) - ;; (insert (funcall org-download-annotate-function link)) - ;; (dolist (attr org-download-image-attr-list) - ;; (insert attr "\n")) - ;; (insert (if (= org-download-image-html-width 0) - ;; "" - ;; (format "#+attr_html: :width %dpx\n" org-download-image-html-width))) - ;; (insert (if (= org-download-image-latex-width 0) - ;; "" - ;; (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width))) - ;; (insert (if (= org-download-image-org-width 0) - ;; "" - ;; (format "#+attr_org: :width %dpx\n" org-download-image-org-width))) - ;; (insert (funcall org-download-markdown-link-format-function filename)) - ;; (org-download--display-inline-images) - ;; (setq str (buffer-substring-no-properties line-beg (point))) - ;; (when in-item-p - ;; (indent-region line-beg (point) indent)) - ;; str)) - - ;; (defun markdown-download-screenshot () - ;; (interactive) - ;; (org-mode) - ;; (org-download-markdown-screenshot) - ;; (markdown-mode)) - - ;;(add-hook 'markdown-mode-hook (lambda () (org-display-inline-images))) - -#+end_src - -** LaTeX in Markdown +*** LaTeX in Markdown +:PROPERTIES: +:CUSTOM_ID: h:8d90fe51-0b32-423a-a159-4f853bc29b68 +:END: #+begin_src emacs-lisp @@ -1514,8 +2022,14 @@ This is a section adapted from org-download to make some org-download functions #+end_src -* Writing ** 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 @@ -1527,6 +2041,12 @@ This is a section adapted from org-download to make some org-download functions #+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 @@ -1536,8 +2056,12 @@ This is a section adapted from org-download to make some org-download functions #+end_src -* Development ** Ripgrep +:PROPERTIES: +:CUSTOM_ID: h:87453f1c-8ea5-4d0a-862d-8973d5bc5405 +:END: + +This is the ripgrep command for Emacs. #+begin_src emacs-lisp @@ -1545,100 +2069,97 @@ This is a section adapted from org-download to make some org-download functions #+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 +#+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 - ;; (setq treesit-language-source-alist - ;; '((bash "https://github.com/tree-sitter/tree-sitter-bash") - ;; (cmake "https://github.com/uyha/tree-sitter-cmake") - ;; (c "https://github.com/tree-sitter/tree-sitter-c") - ;; (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" "master" "src") - ;; (json "https://github.com/tree-sitter/tree-sitter-json") - ;; (make "https://github.com/alemuller/tree-sitter-make") - ;; (markdown "https://github.com/ikatyang/tree-sitter-markdown") - ;; (python "https://github.com/tree-sitter/tree-sitter-python") - ;; (toml "https://github.com/tree-sitter/tree-sitter-toml") - ;; (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") - ;; (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") - ;; (rust "https://github.com/tree-sitter/tree-sitter-rust") - ;; (sql "https://github.com/m-novikov/tree-sitter-sql") - ;; (yaml "https://github.com/ikatyang/tree-sitter-yaml"))) + (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")))) + ) - ;; (add-hook 'rustic-mode-hook 'tree-sitter-mode) - ;; (add-hook 'rustic-mode-hook 'tree-sitter-hl-mode) - - (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")) - (yaml . ("https://github.com/ikatyang/tree-sitter-yaml")))) - ;; :hook (((rustic-mode) . tree-sitter-mode) - ;; ((rustic-mode) . tree-sitter-hl-mode)) - ) - - (use-package treesit-auto - :config - (global-treesit-auto-mode) - (setq treesit-auto-install 'prompt)) + (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 - ;; :init (add-hook 'prog-mode-hook #'direnv-update-environment) :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 - :config - (setq avy-all-windows 'all-frames)) + (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 @@ -1647,14 +2168,44 @@ In order to update the language grammars, run the next command below. #+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) + (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 @@ -1667,51 +2218,40 @@ In order to update the language grammars, run the next command below. ;; 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 #'projectile-dired) ;list files - (setq projectile-switch-project-action #'magit-status)) - - ;; (use-package counsel-projectile - ;; :config (counsel-projectile-mode)) - -#+end_src - -** Project.el - -#+begin_src emacs-lisp - - ;; (use-package project - ;; :ensure nil - ;; :bind - ;; (:map project-prefix-map - ;; ("v" . magit-project-status)) - ;; :config - ;; (add-to-list 'project-switch-commands '(magit-project-status "Magit" "m"))) + (setq projectile-switch-project-action #'magit-status)) #+end_src ** Magit +:PROPERTIES: +:CUSTOM_ID: h:d2c7323d-f8c6-4f23-b70a-930e3e4ecce5 +:END: -- setup permanent github auth using Github CLI https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git#platform-linux -- on fedora: 'sudo dnf install gh' and then 'gh auth login' +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 + (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"))) + ;; 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 @@ -1730,26 +2270,33 @@ 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))) + (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) - ) + :init (setq git-timemachine-show-minibuffer-details t)) #+end_src -** Delimiters (brackets) +** 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 @@ -1783,153 +2330,138 @@ I am not completely sure on electric-pair-mode yet, sometimes it is very helpful #+end_src -** IDE +** rainbow-mode +:PROPERTIES: +:CUSTOM_ID: h:d1a32a69-2f9a-45ef-95fe-a00e3551dc94 +:END: -This section currently holds three different lsp-client implementations; at the moment I am using eglot; it is very sad that there is no end-all solution. All configs are left in for - I like lsp-mode for the debugging and lsp-bridge because asynchronous lsp is quite nice. However eglot is fast enough and I am mostly working on small projects anyways. Still, it is annoying to not have a debugger readily available. - -*** DISABLED Company mode - -The plan is to in the future use Vertico etc. instead and then switch to Corfu. Again, I do not have the time for this yet. +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 company - ;; :after lsp-mode - ;; :hook (lsp-mode . company-mode) - ;; :bind (:map company-active-map - ;; ("" . company-complete-selection)) - ;; (:map lsp-mode-map - ;; ("" . company-indent-or-complete-common)) - ;; :custom - ;; (company-minimum-prefix-length 1) - ;; (company-idle-delay 0.7)) - - ;; (use-package company-box - ;; :hook (company-mode . company-box-mode)) + (use-package rainbow-mode + :config (rainbow-mode)) #+end_src +** Corfu +:PROPERTIES: +:CUSTOM_ID: h:5653d693-ecca-4c95-9633-66b9e3241070 +:END: -*** IN USE 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 == and ==. -Currently unused +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 + ;; :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)) - ) - - - ;; dont disrupt file navigation with completions - (defun swarsel/corfu-normal-return (&optional arg) - (interactive) - (corfu-quit) - (newline) + (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)) ) - ;; (defun swarsel/corfu-complete (&optional arg) - ;; (interactive) - ;; (corfu-complete) - ;; (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)) - - (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))) - - (use-package cape - ;; Bind dedicated completion commands - ;; Alternative prefix keys: C-c p, M-p, M-+, ... - :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) - ) + (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 -*** rust +** 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 @@ -1974,59 +2506,12 @@ Currently unused :mode ("\\.rs" . rustic-mode)) #+end_src -*** Python +** Tramp +:PROPERTIES: +:CUSTOM_ID: h:b9b27a88-06f3-470b-a604-a20b2079bc26 +:END: -#+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 - -*** CUDA - -1. M-x dap-cpptools-setup - -#+begin_src emacs-lisp - - ;; (use-package cuda-mode) - - ;; ;; add path manually; - ;; (add-hook 'cuda-mode-hook - ;; (lambda () - ;; ( setq c-basic-offset 4 - ;; flycheck-cuda-include-path (list ".")) - ;; )) - -#+end_src - -*** 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. #+begin_src emacs-lisp @@ -2057,20 +2542,33 @@ Currently unused #+end_src -*** diff-hl +** 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)) + (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 +** 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 @@ -2079,77 +2577,36 @@ Currently unused #+end_src -*** yasnippet +** 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) - ) - - ;; (use-package yasnippet-snippets) + (yas-reload-all)) #+end_src -The following block is 100% stolen from Dominik :P +**** 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-greek-prefix "'") - ;; (setq wtf/latex-math-prefix "`") (setq wtf/latex-mathbb-prefix "''") (setq swarsel/latex-mathcal-prefix "``") (use-package yasnippet :config - ;; (setq swtf/greek-alphabet - ;; '(("a" . "\\alpha") - ;; ("b" . "\\beta" ) - ;; ("g" . "\\gamma") - ;; ("d" . "\\delta") - ;; ("e" . "\\epsilon") - ;; ("z" . "\\zeta") - ;; ("h" . "\\eta") - ;; ("th" . "\\theta") - ;; ("i" . "\\iota") - ;; ("k" . "\\kappa") - ;; ("l" . "\\lambda") - ;; ("m" . "\\mu") - ;; ("n" . "\\nu") - ;; ("x" . "\\xi") - ;; ("p" . "\\pi") - ;; ("r" . "\\rho") - ;; ("s" . "\\sigma") - ;; ("t" . "\\tau") - ;; ("u" . "\\upsilon") - ;; ("f" . "\\phi") - ;; ("c" . "\\chi") - ;; ("v" . "\\psi") - ;; ("o" . "\\omega"))) - - - ;; The same for capitalized letters - ;; (dolist (elem swtf/greek-alphabet) - ;; (let ((key (car elem)) - ;; (value (cdr elem))) - ;; (when (string-equal key (downcase key)) - ;; (add-to-list 'swtf/greek-alphabet - ;; (cons - ;; (capitalize (car elem)) - ;; (concat - ;; (substring value 0 1) - ;; (capitalize (substring value 1 2)) - ;; (substring value 2))))))) - - ;; (yas-define-snippets - ;; 'latex-mode - ;; (mapcar - ;; (lambda (elem) - ;; (list (concat wtf/latex-greek-prefix (car elem)) (cdr elem) (concat "Greek letter " (car elem)))) - ;; swtf/greek-alphabet)) (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")) @@ -2192,181 +2649,69 @@ The following block is 100% stolen from Dominik :P ("u" . "\\cup") ("0" . "\\emptyset"))) - ;; (yas-define-snippets - ;; 'latex-mode - ;; (mapcar - ;; (lambda (elem) - ;; (let ((key (car elem)) - ;; (value (cdr elem))) - ;; (list (concat wtf/latex-math-prefix key) value (concat "Math symbol " value)))) - ;; swtf/latex-math-symbols)) - ) + ) #+end_src +** eglot -*** Duplicate Lines +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 - (defun duplicate-line (arg) - "Duplicate current line, leaving point in lower line." - (interactive "*p") - - ;; save the point for undo - (setq buffer-undo-list (cons (point) buffer-undo-list)) - - ;; local variables for start and end of line - (let ((bol (save-excursion (beginning-of-line) (point))) - eol) - (save-excursion - - ;; don't use forward-line for this, because you would have - ;; to check whether you are at the end of the buffer - (end-of-line) - (setq eol (point)) - - ;; store the line and disable the recording of undo information - (let ((line (buffer-substring bol eol)) - (buffer-undo-list t) - (count arg)) - ;; insert the line arg times - (while (> count 0) - (newline) ;; because there is no newline in 'line' - (insert line) - (setq count (1- count))) - ) - - ;; create the undo information - (setq buffer-undo-list (cons (cons eol (point)) buffer-undo-list))) - ) ; end-of-let - - ;; put the point in the lowest line and return - (next-line arg)) - -#+end_src - -*** DISABLED lsp-mode - -#+begin_src emacs-lisp - - ;; (use-package lsp-mode - ;; :ensure t - ;; :init - ;; ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l") - ;; (setq lsp-keymap-prefix "C-c l") - ;; :hook (;; replace XXX-mode with concrete major-mode(e. g. python-mode) - ;; (python-mode . lsp) - ;; (c++-mode . lsp) - ;; (c-mode . lsp) - ;; (cuda-mode . lsp) - ;; (rustic-mode . lsp) - ;; ;; if you want which-key integration - ;; (lsp-mode . lsp-enable-which-key-integration)) - ;; :commands lsp) - - ;; (use-package lsp-ui - ;; :config - ;; (setq lsp-ui-doc-enable t - ;; lsp-ui-doc-show-with-cursor t - ;; lsp-ui-doc-delay 0.5 - ;; lsp-ui-doc-max-height 70) - ;; ) - - ;; optionally if you want to use debugger - ;; (use-package dap-mode) - - ;; (use-package lsp-treemacs - ;; :after lsp) - - ;; ;; currently there is a bug with the double click behavior that was recently added. Fixed by this - ;; (autoload 'treemacs-define-doubleclick-action "treemacs-mouse-interface" nil nil) - - ;; ;; (use-package flycheck - ;; :hook - ;; ((lsp-mode text-mode) . flycheck-mode) - ;; :config - ;; (define-key flycheck-mode-map flycheck-keymap-prefix nil) - ;; ;;(setq flycheck-keymap-prefix (kbd my-flycheck-prefix)) - ;; (define-key flycheck-mode-map flycheck-keymap-prefix - ;; flycheck-command-map)) - - ;; ;; (use-package flycheck-posframe - ;; :ensure t - ;; :after flycheck - ;; :config (add-hook 'flycheck-mode-hook #'flycheck-posframe-mode) - ;; :init - ;; (setq - ;; flycheck-posframe-position 'point-top-left-corner - ;; flycheck-posframe-error-prefix "❌ " - ;; flycheck-posframe-info-prefix "ℹ️️️ " - ;; flycheck-posframe-warning-prefix "⚠️ " - ;; flycheck-posframe-prefix "💬 ")) -#+end_src - -*** IN USE eglot - -#+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))) + (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) - (use-package breadcrumb - :config (breadcrumb-mode)) - - #+end_src -*** DISABLED lsp-bridge +** 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 lsp-bridge - ;; :ensure nil - ;; :init - ;; (global-lsp-bridge-mode) - ;; :config - ;; (setq lsp-bridge-enable-debug nil - ;; lsp-bridge-enable-auto-format-code 0 - ;; lsp-bridge-python-lsp-server 'pylsp - ;; lsp-bridge-disable-backup nil - ;; lsp-bridge-enable-org-babel 1 - ;; acm-enable-search-file-words 0 - ;; lsp-bridge-enable-hover-diagnostic 1) - ;; ) - - + (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 @@ -2374,37 +2719,12 @@ The following block is 100% stolen from Dominik :P #+end_src -* File Management -** Dired - -#+begin_src emacs-lisp - - ;; (use-package dired - ;; :ensure nil - ;; :commands (dired dired-jump) - ;; :bind (("C-x C-j" . dired-jump)) - ;; :custom ((dired-listing-switches "-agho --group-directories-first")) - ;; :config - ;; (evil-collection-define-key 'normal 'dired-mode-map - ;; "h" 'dired-single-up-directory - ;; "l" 'dired-single-buffer)) - - ;; (use-package dired-single) - - ;; (use-package nerd-icons-dired - ;; :hook - ;; (dired-mode . nerd-icons-dired-mode)) - ;; (use-package nerd-icons-completion - ;; :after nerd-icons) - ;; (use-package nerd-icons-ivy-rich) - ;; (use-package nerd-icons-ibuffer) - ;; ;; more colorful dired - ;; (use-package diredfl - ;; :hook - ;; (dired-mode . diredfl-mode)) - -#+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 @@ -2414,6 +2734,12 @@ The following block is 100% stolen from Dominik :P :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 @@ -2428,7 +2754,7 @@ The following block is 100% stolen from Dominik :P ("p" "~/Documents/GitHub/" "Projects") ("/" "/" "Root"))) :bind - ((" d" . 'dirvish) + ((" d" . 'dirvish) ("C-=" . 'dirvish-side) :map dirvish-mode-map ("h" . dired-up-directory) @@ -2442,6 +2768,7 @@ The following block is 100% stolen from Dominik :P ("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) @@ -2453,30 +2780,29 @@ The following block is 100% stolen from Dominik :P ("M-j" . dirvish-fd-jump))) #+end_src -** pdf support +** 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)) + (use-package pdf-tools + :init + (if (not (boundp 'pdf-tools-directory)) + (pdf-tools-install)) + :mode ("\\.pdf" . pdf-view-mode)) #+end_src -** open some file types in external programs - -#+begin_src emacs-lisp - - (use-package openwith) - (openwith-mode t) - ;; (setq openwith-associations '(("\\.pdf\\'" "evince" (file)) ("\\.xopp\\'" "xournalpp" (file)))) - (setq openwith-associations '(("\\.xopp\\'" "xournalpp" (file)))) - ;(setq openwith-associations '(("\\.xopp\\'" "xournalpp" (file)))) -#+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 @@ -2484,8 +2810,84 @@ The following block is 100% stolen from Dominik :P #+end_src -* Applications -** Obsidian +** 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 @@ -2509,9 +2911,17 @@ The following block is 100% stolen from Dominik :P #+end_src -** Anki +*** Anki +:PROPERTIES: +:CUSTOM_ID: h:5854c9a6-1319-4961-a112-75b1bf2e1f69 +:END: -*** Basic Anki setup +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 @@ -2555,7 +2965,10 @@ The following block is 100% stolen from Dominik :P #+end_src -*** Own Anki functions +**** 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 @@ -2593,25 +3006,40 @@ The following block is 100% stolen from Dominik :P #+end_src -* Email + Calendar -** make sure mu4e is found +** 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))) + ;; (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 +*** 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 @@ -2625,6 +3053,7 @@ The following block is 100% stolen from Dominik :P (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") @@ -2648,133 +3077,108 @@ The following block is 100% stolen from Dominik :P (:maildir "/Sent Mail" :key ?s) (:maildir "/Trash" :key ?t) (:maildir "/Drafts" :key ?d) - (:maildir "/All Mail" :key ?a)))) + (:maildir "/All Mail" :key ?a))) (setq user-mail-address "leon@swarsel.win" user-full-name "Leon Schwarzäugl") - (setq mu4e-hide-index-messages t) - (setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com)) + (setq mu4e-user-mail-address-list '(leon.schwarzaeugl@gmail.com leon@swarsel.win nautilus.dw@gmail.com mrswarsel@gmail.com))) - (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")) (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 +*** mu4e-alert +:PROPERTIES: +:CUSTOM_ID: h:43209eeb-5d46-472e-b7c2-58a3fb465199 +:END: -Choose the style you prefer for desktop notifications -If you are on Linux you can use -1. notifications - Emacs lisp implementation of the Desktop Notifications API -2. libnotify - Notifications using the `notify-send' program, requires `notify-send' to be in PATH - -On Mac OSX you can set style to -1. notifier - Notifications using the `terminal-notifier' program, requires `terminal-notifier' to be in PATH -1. growl - Notifications using the `growl' program, requires `growlnotify' to be in PATH +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) - (mu4e-alert-set-default-style 'libnotify) + (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 +*** 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 + (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/") - (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)) + ) - :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)))) + ;; ) + ) - (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")))) + (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 -* Startup Application Scripts +** Dashboard: emacs startup screen +:PROPERTIES: +:CUSTOM_ID: h:48f5be2b-b3d2-4276-bd49-2630733f23d5 +:END: -Yep, none currently. +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 :) -* Emacs startup screen #+begin_src emacs-lisp - ;;show mail - ;;(mu4e) (use-package dashboard :ensure t @@ -2837,11 +3241,3 @@ Yep, none currently. #+end_src -* Fix gpg hangup bug - -We need to add this last line, because otherwise gpg will crash on the most recent version when saving a .gpg file within emacs. - -#+begin_src emacs-lisp - (setq gc-cons-threshold (* 800 1000 )) - (fset 'epg-wait-for-status 'ignore) -#+end_src diff --git a/Nix.org b/Nix.org index 586c7df..5523e2b 100644 --- a/Nix.org +++ b/Nix.org @@ -5347,6 +5347,10 @@ As for the `home.sessionVariables`, it should be noted that environment variable source = ../../programs/config/.aspell.conf; target = ".aspell.conf"; }; + ".gitmessage" = { + source = ../../programs/git/.gitmessage; + target = ".gitmessage"; + }; }; home.sessionVariables = { @@ -5416,6 +5420,20 @@ As for the `home.sessionVariables`, it should be noted that environment variable }; userEmail = "leon.schwarzaeugl@gmail.com"; userName = "Swarsel"; + difftastic.enable = true; + lfs.enable = true; + includes = [ + { + contents = { + github = { + user = "Swarsel"; + }; + commit = { + template = "~/.gitmessage"; + }; + }; + } + ]; }; #+end_src diff --git a/profiles/common/home.nix b/profiles/common/home.nix index 6ca766e..b45397a 100644 --- a/profiles/common/home.nix +++ b/profiles/common/home.nix @@ -544,6 +544,10 @@ home.file = { source = ../../programs/config/.aspell.conf; target = ".aspell.conf"; }; + ".gitmessage" = { + source = ../../programs/git/.gitmessage; + target = ".gitmessage"; + }; }; home.sessionVariables = { @@ -606,6 +610,20 @@ programs.git = { }; userEmail = "leon.schwarzaeugl@gmail.com"; userName = "Swarsel"; + difftastic.enable = true; + lfs.enable = true; + includes = [ + { + contents = { + github = { + user = "Swarsel"; + }; + commit = { + template = "~/.gitmessage"; + }; + }; + } + ]; }; programs.fuzzel = { diff --git a/programs/emacs/custom.el b/programs/emacs/custom.el index fa10b22..cdcad2f 100644 --- a/programs/emacs/custom.el +++ b/programs/emacs/custom.el @@ -1,14 +1,14 @@ -(custom-set-variables +;; (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. - '(custom-safe-themes - '("7ec8fd456c0c117c99e3a3b16aaf09ed3fb91879f6601b1ea0eeaee9c6def5d9" - "badd1a5e20bd0c29f4fe863f3b480992c65ef1fa63951f59aa5d6b129a3f9c4c" - "2e05569868dc11a52b08926b4c1a27da77580daa9321773d92822f7a639956ce" - default)) - '(send-mail-function 'sendmail-send-it)) + ;; '(custom-safe-themes + ;; '("7ec8fd456c0c117c99e3a3b16aaf09ed3fb91879f6601b1ea0eeaee9c6def5d9" + ;; "badd1a5e20bd0c29f4fe863f3b480992c65ef1fa63951f59aa5d6b129a3f9c4c" + ;; "2e05569868dc11a52b08926b4c1a27da77580daa9321773d92822f7a639956ce" + ;; default)) + ;; '(send-mail-function 'sendmail-send-it)) ;; (custom-set-faces ;; ;; custom-set-faces was added by Custom. ;; ;; If you edit it by hand, you could mess it up, so be careful. @@ -25,12 +25,12 @@ ;; '(ivy-current-match ((t (:extend t :background "dark cyan" :foreground "black")))) ;; '(org-block ((t (:inherit fixed-pitch :extend t :background "#20282F")))) ;; '(org-block-begin-line ((t (:inherit org-block :extend t :background "#20282F" :foreground "DeepSkyBlue4" :weight bold))))) -(custom-set-faces +;; (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. - '(highlight-indent-guides-even-face ((t (:background "gray10")))) - '(highlight-indent-guides-odd-face ((t (:background "gray20")))) - '(highlight-indent-guides-stack-even-face ((t (:background "gray40")))) - '(highlight-indent-guides-stack-odd-face ((t (:background "gray50"))))) + ;; '(highlight-indent-guides-even-face ((t (:background "gray10")))) + ;; '(highlight-indent-guides-odd-face ((t (:background "gray20")))) + ;; '(highlight-indent-guides-stack-even-face ((t (:background "gray40")))) + ;; '(highlight-indent-guides-stack-odd-face ((t (:background "gray50"))))) diff --git a/programs/emacs/early-init.el b/programs/emacs/early-init.el index 87fb359..1e5fdfd 100644 --- a/programs/emacs/early-init.el +++ b/programs/emacs/early-init.el @@ -1,39 +1,60 @@ +(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) + 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) + ))) (tool-bar-mode 0) (menu-bar-mode 0) (scroll-bar-mode 0) -(setq frame-inhibit-implied-resize t) +(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-frame-alist - (append - (list - '(undecorated . t) - '(min-height . 1) - '(height . 42) - '(min-width . 1) - '(width . 100) - '(vertical-scroll-bars . nil) - '(internal-border-width . 10) - '(tool-bar-lines . 0) - '(menu-bar-lines . 0)))) +(setq-default left-margin-width 1 + right-margin-width 1) - (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)))) - (add-hook - 'after-make-frame-functions - (defun setup-blah-keys (frame) - (with-selected-frame frame - (when (display-graphic-p) - (define-key input-decode-map (kbd "C-i") [C-i]) - (define-key input-decode-map (kbd "C-[") [C-lsb]) - (define-key input-decode-map (kbd "C-m") [C-m]) - )))) - -(defun swarsel/last-buffer () (interactive) (switch-to-buffer nil)) -(global-set-key (kbd "") #'swarsel/last-buffer) - -(setq comp-deferred-compilation nil) +(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]) + )))) diff --git a/programs/emacs/init.el b/programs/emacs/init.el index 1944f87..2877974 100644 --- a/programs/emacs/init.el +++ b/programs/emacs/init.el @@ -1,12 +1,340 @@ -;; The default is 800 kilobytes. Measured in bytes. +(defun swarsel/toggle-evil-state () + (interactive) + (if (or (evil-emacs-state-p) (evil-insert-state-p)) + (evil-normal-state) + (evil-emacs-state))) -;; use UTF-8 everywhere -(set-language-environment "UTF-8") +(defun swarsel/last-buffer () (interactive) (switch-to-buffer nil)) -;; 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") +(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")) + +(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) + +(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)))) + +(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)) + +(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?) + +(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) + +(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)) + +(defun swarsel/org-mode-visual-fill () + (setq visual-fill-column-width 150 + visual-fill-column-center-text t) + (visual-fill-column-mode 1)) + +(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))) + +(defun org-fold-outer () + (interactive) + (org-beginning-of-line) + (if (string-match "^*+" (thing-at-point 'line t)) + (outline-up-heading 1)) + (outline-hide-subtree) + ) + +(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)) + +;; 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)) + +;; 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 + ) ;; set Nextcloud directory for journals etc. (setq swarsel-sync-directory "~/Nextcloud" @@ -23,7 +351,7 @@ swarsel-anki-org-file "Anki.org" swarsel-tasks-org-file "Tasks.org" swarsel-archive-org-file "Archive.org" - swarsel-org-folder-name "org" + swarsel-org-folder-name "Org" swarsel-obsidian-daily-folder-name "⭐ Personal/Journal" swarsel-obsidian-folder-name "Obsidian" swarsel-obsidian-vault-name "Main") @@ -45,74 +373,99 @@ (setq auth-sources '( "~/.emacs.d/.caldav" "~/.emacs.d/.authinfo.gpg") auth-source-cache-expiry nil) ; default is 2h -;; set pandoc for markdown compilation -(setq markdown-command "pandoc") - -;; set org-caldav-sync-initalization -(setq swarsel-caldav-synced 0) - ;; 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 (expand-file-name "programs/emacs/custom.el" swarsel-dotfiles-directory)) +(setq custom-file (make-temp-file "emacs-custom-")) (load custom-file t) -(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 - (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) - ;; less noise when compiling elisp - (setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) - (setq native-comp-async-report-warnings-errors nil) - (setq load-prefer-newer t) +(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 + +;; 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')"))) +;; 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')"))) -;; (scroll-bar-mode -1) -;; (tool-bar-mode -1) -;; (tooltip-mode -1) -;; (menu-bar-mode -1) (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) + +(setq custom-safe-themes t) + +(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 + (setq-default indent-tabs-mode nil tab-width 2) (setq tab-always-indent 'complete) -;; dont send nag when creating python files (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-method 'character) - ;; (setq highlight-indent-guides-character ?|) (setq highlight-indent-guides-responsive 'top) ) -;;(set-face--background 'highlight-indent-guides-odd-face "dark slate gray") -;;(set-face-background 'highlight-indent-guides-even-face "steel blue") -;;(set-face-foreground 'highlight-indent-guides-character-face "dark violet") -;; (setq scroll-step 1 - ;; scroll-margin 4 - ;; scroll-conservatively 101) +(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")) (setq mouse-wheel-scroll-amount '(1 @@ -128,74 +481,48 @@ scroll-conservatively 1 scroll-margin 0 next-screen-context-lines 0) - ;; (setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time - ;; (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling - ;; (setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse - (pixel-scroll-precision-mode 1) - - ;; (use-package fast-scroll - ;; :ensure nil - ;; :init (fast-scroll-mode 1)) - -(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) +(pixel-scroll-precision-mode 1) ;; 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 - ; sane splitting - (setq evil-split-window-below t) - (setq evil-vsplit-window-right t) - :config - (evil-mode 1) - (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state) ; alternative for exiting insert mode - (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join) ; dont show help but instead do normal vim delete backwards - (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) +(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) - ;; evil undo system - (evil-set-undo-system 'undo-tree) - ;; Use visual line motions even outside of visual-line-mode buffers - ;; (evil-global-set-key 'motion "j" 'evil-next-visual-line) - ;; (evil-global-set-key 'motion "k" 'evil-previous-visual-line) + ;; 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))) - ;; 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 - - ; require a specific evil state - (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))) - - ;; Evil configuration for different modes - (use-package evil-collection - :after evil - :config - (evil-collection-init) - (setq forge-add-default-bindings nil)) +(use-package evil-collection + :after evil + :config + (evil-collection-init) + (setq forge-add-default-bindings nil)) ;; enables 2-char inline search (use-package evil-snipe @@ -217,239 +544,35 @@ ;; set the NixOS wordlist by hand (setq ispell-alternate-dictionary "/nix/store/gjmvnbs97cnw19wnqh9m075cdbhy8r8g-wordlist-WORDLIST") -(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?) - -(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) - -(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 - -(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) - -;; 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 - "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") - ;; "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" 'duplicate-line ; duplicate line on CURSOR - "C-M-j" 'consult-buffer - "C-s" 'consult-line - "M-o" 'avy-goto-char-timer - "C-" 'my-python-shell-run - ) - -(defun swarsel/toggle-evil-state () - (interactive) - (if (or (evil-emacs-state-p) (evil-insert-state-p)) - (evil-normal-state) - (evil-emacs-state))) - -(setq inhibit-startup-message t) - -;; (set-fringe-mode nil) ; Give some breathing room - -;; Increase undo limit and allow for more fine grained undo, base emacs deletes way too much on undo -(setq undo-limit 80000000 - evil-want-fine-undo t - auto-save-default t - password-cache-expiry nil - ) - -;; (display-time-mode 1) -(global-subword-mode 1) ; Iterate through CamelCase words - -(use-package rainbow-mode - :config (rainbow-mode)) - - - -(add-hook 'prog-mode-hook 'display-line-numbers-mode) -(add-hook 'text-mode-hook 'display-line-numbers-mode) - -;; (defun swarsel/font-setup (frame) - ;; (set-face-attribute 'default nil :font swarsel-standard-font :height swarsel/default-font-size) - ;; ;; Set the fixed pitch face - basically normal text - ;; (set-face-attribute 'fixed-pitch nil :font swarsel-standard-font :height 140) - ;; ;; Set the variable pitch face - basically headers etc - ;; (set-face-attribute 'variable-pitch nil :font swarsel-alt-font :height 100 :weight 'regular) - ;; (set-face-attribute 'region nil :foreground "cyan" :background "gray40" :weight 'bold) - ;; ;; (remove-hook 'after-make-frame-functions #'swarsel/font-setup) - ;; ) - - ;; (add-hook 'after-make-frame-functions #'swarsel/font-setup) - -;; (defun swarsel/font-setup (frame) (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 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 '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) - ;; (enable-theme 'doom-city-lights) - ;; ) +(set-face-attribute 'variable-pitch nil + :family "IBM Plex Sans" + :weight 'regular + :height 1.06) - ;; (add-hook 'after-make-frame-functions #'swarsel/font-setup) +;; these settings used to be in custom.el (use-package solaire-mode - :defer t - :custom (solaire-global-mode +1)) + :custom + (solaire-global-mode +1)) (use-package doom-themes - :defer t - :hook (server-after-make-frame . (lambda () (load-theme - 'doom-city-lights t))) - ) + :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)) -;; (set-frame-parameter (selected-frame) 'alpha '(95 . 95)) -;; (add-to-list 'default-frame-alist '(alpha . (95 . 95))) -;; (set-frame-parameter (selected-frame) 'fullscreen 'maximized) -;; (add-to-list 'default-frame-alist '(fullscreen . maximized)) +(use-package nerd-icons) (use-package mixed-pitch :custom @@ -458,22 +581,15 @@ :hook (text-mode . mixed-pitch-mode)) -(use-package nerd-icons) - -;; Adds a more beautiful modeline with less clutter (use-package doom-modeline - :init (doom-modeline-mode) + :init + (doom-modeline-mode) + (column-number-mode) :custom ((doom-modeline-height 22) (doom-modeline-indent-info nil) (doom-modeline-buffer-encoding nil))) -;; Generally show line numbers -(column-number-mode) - -;; (unless (string-match-p "^Power N/A" (battery)) ; On laptops... -;; (display-battery-mode 1)) - (setq read-buffer-completion-ignore-case t read-file-name-completion-ignore-case t completion-ignore-case t) @@ -488,116 +604,73 @@ (vertico-mode) (vertico-mouse-mode)) - (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)) +(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)) - (use-package orderless - :custom - (completion-styles '(orderless flex basic)) - (completion-category-overrides '((file (styles . (partial-completion))) - (eglot (styles orderless))))) +(use-package orderless + :custom + (completion-styles '(orderless flex basic)) + (completion-category-overrides '((file (styles . (partial-completion))) + (eglot (styles orderless))))) - (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) - ("M-g M-g" . consult-goto-line) - ("M-g i" . consult-imenu) - ("M-s s" . consult-line) - ("M-s M-s" . consult-line-multi) -:map minibuffer-local-map - ("C-j" . next-line) - ("C-k" . previous-line))) +(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))) - (use-package embark - :bind - (("C-." . embark-act) - ("M-." . embark-dwim) - ("C-h B" . embark-bindings)) - :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))))) +(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))))) - (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)) +(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)) - (use-package marginalia - :after vertico - :init - (marginalia-mode) - (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) +(use-package marginalia + :after vertico + :init + (marginalia-mode) + (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) - (use-package nerd-icons-completion - :after (marginalia nerd-icons) - :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) - :init - (nerd-icons-completion-mode)) - -;; (use-package ivy -;; :init (ivy-mode 1) -;; :diminish -;; :bind (("C-s" . swiper) ; call swiper (find tool) -;; :map ivy-minibuffer-map -;; ;("TAB" . ivy-alt-done) ; autocomplete -;; ("C-l" . ivy-alt-done) -;; ("C-" . ivy-alt-done) ; for kyria -;; ("C-h" . counsel-up-directory) ; for kyria -;; ("C-" . counsel-up-directory) ; for kyria -;; ("C-j" . ivy-next-line) ; go up and down in ivy using vim keys -;; ("C-" . ivy-next-line) ; for kyria -;; ("C-k" . ivy-previous-line) -;; ("C-" . ivy-previous-line) ; for kyria -;; :map ivy-switch-buffer-map -;; ("C-k" . ivy-previous-line) -;; ("C-" . ivy-previous-line) ; for kyria -;; ("C-l" . ivy-done) -;; ("C-" . ivy-done) ; for kyria -;; ("C-d" . ivy-switch-buffer-kill) -;; :map ivy-reverse-i-search-map -;; ("C-k" . ivy-previous-line) -;; ("C-" . ivy-previous-line) ; for kyria -;; ("C-d" . ivy-reverse-i-search-kill)) -;; :config -;; (setq ivy-use-virtual-buffers t) -;; (setq ivy-count-format "(%d/%d) ") -;; (setq ivy-wrap t)) - -;; ;; More information about functions in ivy-mode -;; (use-package ivy-rich -;; :init -;; (ivy-rich-mode 1)) - -;; (use-package counsel -;; :init (counsel-mode 1) -;; :bind (("C-M-j" . counsel-switch-buffer) -;; ("M-x" . counsel-M-x) -;; ("C-x b" . counsel-ibuffer) -;; ("C-x C-f" . counsel-find-file) -;; :map minibuffer-local-map -;; ("C-r" . 'counsel-minibuffer-history)) -;; :config -;; (setq ivy-initial-inputs-alist nil)) +(use-package nerd-icons-completion + :after (marginalia nerd-icons) + :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) + :init + (nerd-icons-completion-mode)) (use-package which-key :init (which-key-mode) @@ -605,16 +678,6 @@ :config (setq which-key-idle-delay 0.3)) -;; (use-package helpful -;; :custom -;; (counsel-describe-function-function #'helpful-callable) -;; (counsel-describe-variable-function #'helpful-variable) -;; :bind -;; ([remap describe-function] . counsel-describe-function) -;; ([remap describe-command] . helpful-command) -;; ([remap describe-variable] . counsel-describe-variable) -;; ([remap describe-key] . helpful-key)) - (use-package helpful :bind (("C-h f" . helpful-callable) @@ -624,16 +687,9 @@ :config (setq help-window-select nil)) -(use-package hydra) - -;; 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)) - (use-package ligature + :init + (global-ligature-mode t) :config (ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "