.dotfiles/Emacs.org
2024-05-05 02:31:19 +02:00

84 KiB
Raw Blame History

Emacs Configuration

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]

    • English across the entire file
    • Stay in first person
  • Formatting [3/3]

    • One free line at start and end of each source block
    • Uniform indentation across file
    • One free line at the start and end of each header
  • Finish all TODO blocks in this file

Initialization

Startup performance

  ;; The default is 800 kilobytes.  Measured in bytes.

Directory setup

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

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

  ;; set pandoc for markdown compilation
  (setq markdown-command "pandoc")

  ;; set org-caldav-sync-initalization
  (setq swarsel-caldav-synced 0)

Package System Setup

This is handles by nix, no configuration is needed here. Do note that package.el must stay enabled in early-init.el.

Keeping .emacs.d clean

  ;; 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))
  (load custom-file t)

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.
  (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
  (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')")))

Disable GUI distractions

These settings are mostly useless in my eyes and provide little more than a distraction.

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

Indentation

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

Scrolling

By default, emacs scrolls half a page when reaching the bottom of the buffer. This is extremely annoying.

  (setq scroll-step 1
        scroll-margin 4
        scroll-conservatively 101)

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

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)

Evil

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

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

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

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

Move backup files to another location

  (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

Custom Keybindings

  ;; 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
      "t"  '(:ignore t :which-key "toggles")
      "ts" '(hydra-text-scale/body :which-key "scale text")
      "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")
      ;; "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 notetye 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
   "C-<f9>" 'my-python-shell-run
   )

UI

General

  • This sets up some basic UI elements
  (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)

Looks

Font Configuration

    ;; (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 '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)
    ;; )

    ;; (add-hook 'after-make-frame-functions #'swarsel/font-setup)

Color Theme

  (use-package solaire-mode
    :defer t
    :custom (solaire-global-mode +1))

  (use-package doom-themes
    :defer t
    :hook (server-after-make-frame . (lambda () (load-theme
                                                 'doom-city-lights t)))
    )

Transparent background

This is handled by Nix and no longer needed here.

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

Variable Pitch Mode

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

Modeline

Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline.

  (use-package nerd-icons)

  ;; Adds a more beautiful modeline with less clutter
  (use-package doom-modeline
    :init (doom-modeline-mode)
    :custom
    ((doom-modeline-height 22)
     (doom-modeline-indent-info t)))

  ;; Generally show line numbers
  (column-number-mode)

  ;; (unless (string-match-p "^Power N/A" (battery))   ; On laptops...
  ;;   (display-battery-mode 1))

Helper Modes (Ivy…, Vertico…, which-key, helpful)

Vertico and friends

Soon I want to try out this new hot stuff - just at the moment there is too much other stuff to do.

    (use-package vertico
      :custom
      (vertico-scroll-margin 0)
      (vertico-count 10)
      (vertico-resize t)
      (vertico-cycle t)
      :init
      (vertico-mode)
      (vertico-mouse-mode))

    (use-package vertico-directory
      :ensure nil
      :after vertico
      :bind (:map vertico-map
                  ("RET" . vertico-directory-enter)
                  ("DEL" . vertico-directory-delete-char)
                  ("M-DEL" . vertico-directory-delete-word))
      ;; 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 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-outline)
      ("M-g M-g" . consult-goto-line)
      ("M-g i" . consult-imenu)
      ("M-s s" . consult-line)
      ("M-s M-s" . consult-line-multi))

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

Ivy + Counsel

  ;; (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-<right>" . ivy-alt-done) ; for kyria
  ;;          ("C-h" . counsel-up-directory) ; for kyria
  ;;          ("C-<left>" . counsel-up-directory) ; for kyria
  ;;          ("C-j" . ivy-next-line) ; go up and down in ivy using vim keys
  ;;          ("C-<down>" . ivy-next-line) ; for kyria
  ;;          ("C-k" . ivy-previous-line)
  ;;          ("C-<up>" . ivy-previous-line) ; for kyria
  ;;          :map ivy-switch-buffer-map
  ;;          ("C-k" . ivy-previous-line)
  ;;          ("C-<up>" . ivy-previous-line) ; for kyria
  ;;          ("C-l" . ivy-done)
  ;;          ("C-<right>" . ivy-done) ; for kyria
  ;;          ("C-d" . ivy-switch-buffer-kill)
  ;;          :map ivy-reverse-i-search-map
  ;;          ("C-k" . ivy-previous-line)
  ;;          ("C-<up>" . 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))

Helpful + which-key: Better help defaults

  (use-package which-key
    :init (which-key-mode)
    :diminish which-key-mode
    :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
    :custom
    (help-select-window t)
    :bind
    (("C-h f" . helpful-callable)
     ("C-h v" . helpful-variable)
     ("C-h k" . helpful-key)
     ("C-h C-." . helpful-at-point)))

Text Scaling

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

Ligatures

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

Popup + Shackle 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))

Indicate first and last line of buffer

(setq-default indicate-buffer-boundaries t)

Org Mode

General

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

    ;; (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) "•")))))))

    (use-package org
      ;;:diminish (org-indent-mode)
      :hook (org-mode . swarsel/org-mode-setup)
      :bind ("C-<tab>" . 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" "$" "$$" "\\(" "\\[")))

      ;; (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"))


      ;; (require 'org-habit)
      ;; (add-to-list 'org-modules 'org-habit)
      ;; (setq org-habit-graph-column 60)

      ;; (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@)")))

      ;; (setq org-refile-targets
      ;;       '((swarsel-archive-org-file :maxlevel . 1)
      ;;         (swarsel-anki-org-file :maxlevel . 1)
      ;;         (swarsel-tasks-org-file :maxlevel . 1)))

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

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

      ;;         ("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)))))))

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

      ;;         ("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)
      )

Font Faces

  ;; ;; Set faces for heading levels
  ;; (with-eval-after-load 'org-faces  (dolist (face '((org-level-1 . 1.3)
  ;;                                                   (org-level-2 . 1.2)
  ;;                                                   (org-level-3 . 1.15)
  ;;                                                   (org-level-4 . 1.1)
  ;;                                                   (org-level-5 . 1.1)
  ;;                                                   (org-level-6 . 1.1)
  ;;                                                   (org-level-7 . 1.1)
  ;;                                                   (org-level-8 . 1.1)))
  ;;                                     (set-face-attribute (car face) nil :font swarsel-alt-font :weight 'regular :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 '(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)))

org-appear

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

Heading Bullets

  ;; (use-package org-bullets
  ;;   :after org
  ;;   :hook (org-mode . org-bullets-mode)
  ;;   :custom
  ;;   (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●")))

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

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

Fix headings not folding sometimes

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

Babel

Language Configuration

  • This configures the languages that babel recognizes
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)))

  (push '("conf-unix" . conf-unix) org-src-lang-modes)

old easy structure templates

  • org 9.2 changed the way structure templates work. This brings back the old way it worked.

      (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"))
      (add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle"))

Auto-tangle Configuration Files

  • Automatically tangles all configuration blocks in this file to the defined Emacs orgfile
  (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)))

aucTex

    ;; (use-package auctex
    ;;   :ensure nil)
    (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 'turn-on-reftex)
    ;; (setq reftex-plug-into-AUCTeX t)

TeX

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

org-download

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

org-fragtog

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

Fold current heading

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

org-modern

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

Nix Mode

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

Markdown Mode

Mode

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

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.

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

Writing

Olivetti

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

darkroom

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

Development

Ripgrep

  (use-package rg)

Tree-sitter

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

  (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
      ;;   (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")))

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

direnv

  (use-package direnv
    ;; :init (add-hook 'prog-mode-hook #'direnv-update-environment)
    :custom (direnv-always-show-summary nil)
    :config (direnv-mode))

devdocs

(use-package devdocs)

Projectile

  (use-package projectile
    :diminish projectile-mode
    :config (projectile-mode)
    :custom ((projectile-completion-system 'ivy)) ;; 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 #'projectile-dired) ;list files
    (setq projectile-switch-project-action #'magit-status))

  ;; (use-package counsel-projectile
  ;;   :config (counsel-projectile-mode))

Project.el

  ;; (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")))

Magit

  (use-package magit
    :config
    (setq magit-repository-directories `((,swarsel-projects-directory  . 1)
                                         (,swarsel-emacs-directory . 0)
                                         (,swarsel-obsidian-directory . 0)))
    :custom
    (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ; stay in the same window

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

git-timemachine

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

Delimiters (brackets)

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

IDE

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.

  ;; (use-package company
  ;;   :after lsp-mode
  ;;   :hook (lsp-mode . company-mode)
  ;;   :bind (:map company-active-map
  ;;               ("<tab>" . company-complete-selection))
  ;;   (:map lsp-mode-map
  ;;         ("<tab>" . 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))

IN USE Corfu

Currently unused

      ;; (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)
                    ("<up>"      . swarsel/corfu-quit-and-up)
                    ("<down>"     . swarsel/corfu-quit-and-down))
        )


    ;; dont disrupt file navigation with completions
    (defun swarsel/corfu-normal-return (&optional arg)
      (interactive)
      (corfu-quit)
      (newline)
      )

    ;; (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-c p p" . completion-at-point) ;; capf
    ;;        ("C-c p t" . complete-tag)        ;; etags
    ;;        ("C-c p d" . cape-dabbrev)        ;; or dabbrev-completion
    ;;        ("C-c p h" . cape-history)
    ;;        ("C-c p f" . cape-file)
    ;;        ("C-c p k" . cape-keyword)
    ;;        ("C-c p s" . cape-elisp-symbol)
    ;;        ("C-c p e" . cape-elisp-block)
    ;;        ("C-c p a" . cape-abbrev)
    ;;        ("C-c p l" . cape-line)
    ;;        ("C-c p w" . cape-dict)
    ;;        ("C-c p :" . cape-emoji)
    ;;        ("C-c p \\" . cape-tex)
    ;;        ("C-c p _" . cape-tex)
    ;;        ("C-c p ^" . cape-tex)
    ;;        ("C-c p &" . cape-sgml)
    ;;        ("C-c p r" . cape-rfc1345))
    :init
    ;; 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)
  )

rust

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

Python

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

CUDA

  1. M-x dap-cpptools-setup
  ;; (use-package cuda-mode)

  ;; ;; add path manually;
  ;; (add-hook 'cuda-mode-hook
  ;;           (lambda ()
  ;;             ( setq c-basic-offset              4
  ;;               flycheck-cuda-include-path (list "."))
  ;;             ))

Tramp

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

Commenting

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

yasnippet

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

        ;; (use-package yasnippet-snippets)

The following block is 100% stolen from Dominik :P

  (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")
            ("t" . "\\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"))

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


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

Duplicate Lines

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

DISABLED lsp-mode

    ;; (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 "💬 "))

IN USE eglot

    (use-package eglot
      :ensure nil
      :hook
      ((python-mode
        c-mode
        c++-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 breadcrumb
    :config (breadcrumb-mode))

DISABLED lsp-bridge

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

Prevent breaking of hardlinks

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

File Management

Dired

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

Dirvish

  (use-package dirvish
    :init
    (dirvish-override-dired-mode)
    :config
    (dirvish-peek-mode)
    (dirvish-side-follow-mode)
    :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
    (("<C-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)
     ("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)))

pdf support

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

open some file types in external programs

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

Jupyter

  (use-package ein)

Applications

Obsidian

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

Anki

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)

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

Email + Calendar

make sure mu4e is found

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

mu4e

  (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-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.schwarzaeugl@gmail.com"
        user-full-name "Leon Schwarzäugl")

mu4e-alert

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
  2. growl - Notifications using the `growl' program, requires `growlnotify' to be in PATH
  (use-package mu4e-alert)
  (mu4e-alert-set-default-style 'libnotify)
  (add-hook 'after-init-hook #'mu4e-alert-enable-notifications)

  (mu4e t)

Calendar

        (use-package org-caldav
          :init

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

Startup Application Scripts

Yep, none currently.

Emacs startup screen

    ;;show mail
    ;;(mu4e)

  (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-set-navigator t
          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")))
             )
            )))
    (setq dashboard-projects-switch-function 'counsel-projectile-switch-project-by-name)

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.

   (setq gc-cons-threshold (* 800 1000 ))
   (fset 'epg-wait-for-status 'ignore)