.dotfiles/Emacs.org
Swarsel 4cdfd1e9ea
Several Improvements to Emacs and NixOX
Emacs:
Fix: Emacs mouse wheel scrolling was overshooting sometimes
Feat: Hide more useless messages in the echo are (NO CHILDREN)
Feat: Add presentation mode using org-present
Fix: Fix org-tempo angled brackets insertion
Feat: better math mode in AucTeX in lieu of some yasnippet bits
Fix: add treesitter modes to eglot for automatic startup
Fix: Make mu4e automatic sender address choosing more robust

NixOS:
Feat: Add oama derivation
Feat: Add main email sender address to config
Feat: Add opacity toggle (for use with presentation mode)
2024-06-01 01:55:32 +02:00

2809 lines
95 KiB
Org Mode
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+title: Emacs Configuration
#+PROPERTY: header-args:emacs-lisp :tangle programs/emacs/init.el :mkdirp yes
** 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
* Initialization
** Startup performance
#+begin_src emacs-lisp
;; The default is 800 kilobytes. Measured in bytes.
#+end_src
** Directory setup
#+begin_src emacs-lisp
;; use UTF-8 everywhere
(set-language-environment "UTF-8")
;; set default font size
(defvar swarsel/default-font-size 130)
(setq swarsel-standard-font "FiraCode Nerd Font Mono"
swarsel-alt-font "FiraCode Nerd Font Mono")
;; 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)
#+end_src
** 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
#+begin_src emacs-lisp
;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d
(setq user-emacs-directory (expand-file-name "~/.cache/emacs/")
url-history-file (expand-file-name "url/history" user-emacs-directory))
;; Use no-littering to automatically set common paths to the new user-emacs-directory
(use-package no-littering)
(setq custom-file (expand-file-name "programs/emacs/custom.el" swarsel-dotfiles-directory))
(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
;; 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
#+begin_src emacs-lisp
(let ((backup-dir "~/tmp/emacs/backups")
(auto-saves-dir "~/tmp/emacs/auto-saves/"))
(dolist (dir (list backup-dir auto-saves-dir))
(when (not (file-directory-p dir))
(make-directory dir t)))
(setq backup-directory-alist `(("." . ,backup-dir))
auto-save-file-name-transforms `((".*" ,auto-saves-dir t))
auto-save-list-file-prefix (concat auto-saves-dir ".saves-")
tramp-backup-directory-alist `((".*" . ,backup-dir))
tramp-auto-save-directory auto-saves-dir))
(setq backup-by-copying t ; Don't delink hardlinks
delete-old-versions t ; Clean up the backups
version-control t ; Use version numbers on backups,
kept-new-versions 5 ; keep some new versions
kept-old-versions 2) ; and some old ones, too
#+end_src
* Custom Keybindings
#+begin_src emacs-lisp
;; 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")
"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-<f9>" 'my-python-shell-run
)
#+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
)
;; (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)
#+end_src
** Looks
*** Font Configuration
- 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'
#+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)
;; )
;; (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)
#+end_src
*** Color Theme
#+begin_src emacs-lisp
(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)))
)
#+end_src
*** Transparent background
This is handled by Nix and no longer needed here.
#+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))
#+end_src
*** Variable Pitch Mode
#+begin_src emacs-lisp
(use-package mixed-pitch
:custom
(mixed-pitch-set-height nil)
(mixed-pitch-variable-pitch-cursor nil)
:hook
(text-mode . mixed-pitch-mode))
#+end_src
** Modeline
Here I set up the modeline with some information that I find useful. Specficially I am using the doom modeline.
#+begin_src emacs-lisp
(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 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
Soon I want to try out this new hot stuff - just at the moment there is too much other stuff to do.
#+begin_src emacs-lisp
(setq read-buffer-completion-ignore-case t
read-file-name-completion-ignore-case t
completion-ignore-case t)
(use-package vertico
:custom
(vertico-scroll-margin 0)
(vertico-count 10)
(vertico-resize t)
(vertico-cycle t)
:init
(vertico-mode)
(vertico-mouse-mode))
(use-package vertico-directory
:ensure nil
:after vertico
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-word)
("M-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 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))
#+end_src
*** Ivy + Counsel
#+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-<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))
#+end_src
*** Helpful + which-key: Better help defaults
#+begin_src emacs-lisp
(use-package which-key
:init (which-key-mode)
:diminish which-key-mode
:config
(setq which-key-idle-delay 0.3))
;; (use-package helpful
;; :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)))
#+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
#+begin_src emacs-lisp
(use-package ligature
:config
(ligature-set-ligatures 'prog-mode
'("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".." ".?" "+>" "++" "?:" "?="
"?." "??" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" "\\\\"
"://" ";;"))
(global-ligature-mode t))
#+end_src
** Popup + Shackle Buffers
#+begin_src emacs-lisp
(use-package popper
:bind (("M-[" . popper-toggle))
:init
(setq popper-reference-buffers
'("\\*Messages\\*"
("\\*Warnings\\*" . hide)
"Output\\*$"
"\\*Async Shell Command\\*"
"\\*Async-native-compile-log\\*"
help-mode
helpful-mode
"*Occur*"
"*scratch*"
"*julia*"
"*Python*"
;; ("*tex-shell*" . hide)
(compilation-mode . hide)))
(popper-mode +1)
(popper-echo-mode +1))
(use-package shackle
:config
(setq shackle-rules '(("*Messages*" :select t :popup t :align right :size 0.3)
("*Warnings*" :ignore t :popup t :align right :size 0.3)
("*Occur*" :select t :popup t :align below :size 0.2)
("*scratch*" :select t :popup t :align below :size 0.2)
("*Python*" :select t :popup t :align below :size 0.2)
("*tex-shell*" :ignore t :popup t :align below :size 0.2)
(helpful-mode :select t :popup t :align right :size 0.35)
(help-mode :select t :popup t :align right :size 0.4)))
(shackle-mode 1))
#+end_src
** Indicate first and last line of buffer
#+begin_src emacs-lisp
(setq-default indicate-buffer-boundaries t)
#+end_src
* Org Mode
** General
#+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)
;; (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)
)
#+end_src
** Font Faces
#+begin_src emacs-lisp
;; Set faces for heading levels
(with-eval-after-load 'org-faces (dolist (face '((org-level-1 . 1.1)
(org-level-2 . 0.9)
(org-level-3 . 0.9)
(org-level-4 . 0.9)
(org-level-5 . 0.9)
(org-level-6 . 0.9)
(org-level-7 . 0.9)
(org-level-8 . 0.9)))
(set-face-attribute (car face) nil :font swarsel-alt-font :weight 'medium :height (cdr face)))
;; Ensure that anything that should be fixed-pitch in Org files appears that way
(set-face-attribute 'org-block nil :inherit 'fixed-pitch)
(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
(set-face-attribute 'org-formula nil :inherit 'fixed-pitch)
(set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch))
#+end_src
** org-appear
#+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)
;; )
#+end_src
** Heading Bullets
#+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
#+begin_src emacs-lisp
(setq org-fold-core-style 'overlays)
#+end_src
** Babel
*** Language Configuration
- This configures the languages that babel recognizes
#+begin_src emacs-lisp
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(shell . t)
))
(push '("conf-unix" . conf-unix) org-src-lang-modes)
#+end_src
*** old easy structure templates
- org 9.2 changed the way structure templates work. This brings back the old way it worked.
#+begin_src emacs-lisp
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python :results output"))
(add-to-list 'org-structure-template-alist '("nix" . "src nix :tangle"))
#+end_src
** Auto-tangle Configuration Files
- 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
#+begin_src emacs-lisp
(use-package auctex)
(setq TeX-auto-save t)
(setq TeX-save-query nil)
(setq TeX-parse-self t)
(setq-default TeX-master nil)
(add-hook 'LaTeX-mode-hook 'visual-line-mode)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
(add-hook 'LaTeX-mode-hook 'reftex-mode)
(setq LaTeX-electric-left-right-brace t)
(setq font-latex-fontify-script nil)
(setq TeX-electric-sub-and-superscript t)
;; (setq reftex-plug-into-AUCTeX t)
#+end_src
** org-download
#+begin_src emacs-lisp
(use-package org-download
:after org
:defer nil
:custom
(org-download-method 'directory)
(org-download-image-dir "./images")
(org-download-heading-lvl 0)
(org-download-timestamp "org_%Y%m%d-%H%M%S_")
;;(org-image-actual-width 500)
(org-download-screenshot-method "grim -g \"$(slurp)\" %s")
:bind
("C-M-y" . org-download-screenshot)
:config
(require 'org-download))
#+end_src
** org-fragtog
#+begin_src emacs-lisp
(use-package org-fragtog)
(add-hook 'org-mode-hook 'org-fragtog-mode)
(add-hook 'markdown-mode-hook 'org-fragtog-mode)
#+end_src
** Fold current heading
#+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
#+begin_src emacs-lisp
(use-package org-modern
:config (setq org-modern-block-name
'((t . t)
("src" "»" "")))
:hook (org-mode . org-modern-mode))
#+end_src
** Presentations
#+begin_src emacs-lisp
(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)
#+end_src
* Nix Mode
#+begin_src emacs-lisp
(use-package nix-mode
:mode "\\.nix\\'")
#+end_src
* Markdown Mode
** Mode
#+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)))
#+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
#+begin_src emacs-lisp
(add-hook 'markdown-mode-hook
(lambda ()
(local-set-key (kbd "C-c C-x C-l") 'org-latex-preview)
(local-set-key (kbd "C-c C-x C-u") 'markdown-toggle-url-hiding)
))
#+end_src
* Writing
** Olivetti
#+begin_src emacs-lisp
(use-package olivetti
:init
(setq olivetti-body-width 100)
(setq olivetti-recall-visual-line-mode-entry-state t))
#+end_src
** darkroom
#+begin_src emacs-lisp
(use-package darkroom
:init
(setq darkroom-text-scale-increase 3))
#+end_src
* Development
** Ripgrep
#+begin_src emacs-lisp
(use-package rg)
#+end_src
** Tree-sitter
In order to update the language grammars, run the next command below.
#+begin_src emacs-lisp :tangle no
(mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
#+end_src
#+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")))
;; (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))
#+end_src
** direnv
#+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
#+begin_src emacs-lisp
(use-package avy
:config
(setq avy-all-windows 'all-frames))
#+end_src
** crdt (Collaborative Editing)
#+begin_src emacs-lisp
(use-package crdt)
#+end_src
** devdocs
#+begin_src emacs-lisp
(use-package devdocs)
#+end_src
** Projectile
#+begin_src emacs-lisp
(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))
#+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")))
#+end_src
** Magit
- 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'
#+begin_src emacs-lisp
(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
;; yubikey support for pushing commits
;; commiting is enabled through nixos gpg-agent config
(setenv "SSH_AUTH_SOCK" (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))
#+end_src
** Forge
NOTE: Make sure to configure a GitHub token before using this package!
- https://magit.vc/manual/forge/Token-Creation.html#Token-Creation
- https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started
- https://magit.vc/manual/ghub/Storing-a-Token.html
- https://www.emacswiki.org/emacs/GnuPG
(1) in practice: github -<> settings -<> developer option -<>
create classic token with repo; user; read:org permissions
(2) install GnuGP (and add to PATH)
(3) create ~/.authinfo.gpg with the following info scheme:
machine api.github.com login USERNAME^forge password 012345abcdef...
#+begin_src emacs-lisp
(use-package forge
:after magit)
(with-eval-after-load 'forge
(add-to-list 'forge-alist
'("sgit.iue.tuwien.ac.at"
"sgit.iue.tuwien.ac.at/api/v1"
"sgit.iue.tuwien.ac.at"
forge-gitea-repository)))
#+end_src
** git-timemachine
#+begin_src emacs-lisp
(use-package git-timemachine
:hook (git-time-machine-mode . evil-normalize-keymaps)
:init (setq git-timemachine-show-minibuffer-details t)
)
#+end_src
** Delimiters (brackets)
- rainbow-delimiters colors all delimiters, also ones not in current selection
- paren highlights the current delimiter selection especially bold
- highlight-parentheses boldly highlights all delimiters in current selection
I am not completely sure on electric-pair-mode yet, sometimes it is very helpful, sometimes it annoys me to no end.
#+begin_src emacs-lisp
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
(use-package highlight-parentheses
:config
(setq highlight-parentheses-colors '("black" "white" "black" "black" "black" "black" "black"))
(setq highlight-parentheses-background-colors '("magenta" "blue" "cyan" "green" "yellow" "orange" "red"))
(global-highlight-parentheses-mode t))
(electric-pair-mode 1)
(setq electric-pair-preserve-balance nil)
;; don't try to be overly smart
(setq electric-pair-delete-adjacent-pairs nil)
;; don't skip newline when auto-pairing parenthesis
(setq electric-pair-skip-whitespace-chars '(9 32))
;; in org-mode buffers, do not pair < and > in order not to interfere with org-tempo
(add-hook 'org-mode-hook (lambda ()
(setq-local electric-pair-inhibit-predicate
`(lambda (c)
(if (char-equal c ?<) t (,electric-pair-inhibit-predicate c))))))
#+end_src
** 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.
#+begin_src emacs-lisp
;; (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))
#+end_src
*** IN USE Corfu
Currently unused
#+begin_src emacs-lisp
;; (use-package corfu
;; :custom
;; (corfu-cycle t)
;; :init
;; (global-corfu-mode))
(use-package corfu
:init
(global-corfu-mode)
(corfu-history-mode)
(corfu-popupinfo-mode) ; Popup completion info
:custom
(corfu-auto t)
(corfu-auto-prefix 3)
(corfu-auto-delay 0.3)
(corfu-cycle t)
(corfu-quit-no-match 'separator)
(corfu-separator ?\s)
;; (corfu-quit-no-match t)
(corfu-popupinfo-max-height 70)
(corfu-popupinfo-delay '(0.5 . 0.2))
;; (corfu-preview-current 'insert) ; insert previewed candidate
(corfu-preselect 'prompt)
(corfu-on-exact-match nil) ; Don't auto expand tempel snippets
;; Optionally use TAB for cycling, default is `corfu-complete'.
:bind (:map corfu-map
("M-SPC" . corfu-insert-separator)
("<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))
)
;; 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)
;; 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
#+begin_src emacs-lisp
;; (use-package rustic
;; :ensure
;; :bind (:map rustic-mode-map
;; ("M-j" . lsp-ui-imenu)
;; ("M-?" . lsp-find-references)
;; ("C-c C-c l" . flycheck-list-errors)
;; ("C-c C-c a" . lsp-execute-code-action)
;; ("C-c C-c r" . lsp-rename)
;; ("C-c C-c q" . lsp-workspace-restart)
;; ("C-c C-c Q" . lsp-workspace-shutdown)
;; ("C-c C-c s" . lsp-rust-analyzer-status))
;; :config
;; (setq rustic-format-on-save t)
;; (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))
;; (defun rk/rustic-mode-hook ()
;; ;; so that run C-c C-c C-r works without having to confirm, but don't try to
;; ;; save rust buffers that are not file visiting. Once
;; ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should
;; ;; no longer be necessary.
;; (when buffer-file-name
;; (setq-local buffer-save-without-query t))
;; (add-hook 'before-save-hook 'lsp-format-buffer nil t))
;; (use-package rustic
;; :config
;; (setq rustic-format-on-save t)
;; (setq rustic-lsp-client 'eglot)
;; :custom
;; (lsp-rust-analyzer-cargo-watch-command "clippy")
;; (lsp-rust-analyzer-server-display-inlay-hints t)
;; :mode ("\\.rs" . rustic-mode))
(use-package rustic
:config
(setq rustic-format-on-save t)
(setq rustic-lsp-client 'eglot)
:mode ("\\.rs" . rustic-mode))
#+end_src
*** Python
#+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
#+begin_src emacs-lisp
(use-package tramp
:init
(setq vc-ignore-dir-regexp
(format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
(setq tramp-default-method "ssh")
(setq tramp-auto-save-directory
(expand-file-name "tramp-auto-save" user-emacs-directory))
(setq tramp-persistency-file-name
(expand-file-name "tramp-connection-history" user-emacs-directory))
(setq password-cache-expiry nil)
(setq tramp-use-ssh-controlmaster-options nil)
(setq remote-file-name-inhibit-cache nil)
:config
(customize-set-variable 'tramp-ssh-controlmaster-options
(concat
"-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p "
"-o ControlMaster=auto -o ControlPersist=yes"))
)
#+end_src
*** diff-hl
#+begin_src emacs-lisp
(use-package diff-hl
:hook
((prog-mode
org-mode) . diff-hl-mode)
:init
(diff-fl-flydiff-mode)
(diff-hl-margin-mode)
(diff-hl-show-hunk-mouse-mode))
#+end_src
*** Commenting
#+begin_src emacs-lisp
(use-package evil-nerd-commenter
:bind ("M-/" . evilnc-comment-or-uncomment-lines))
#+end_src
*** yasnippet
#+begin_src emacs-lisp
(use-package yasnippet
:init (yas-global-mode 1)
:config
(yas-reload-all)
)
;; (use-package yasnippet-snippets)
#+end_src
The following block is 100% stolen from Dominik :P
#+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"))
(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))
)
#+end_src
*** Duplicate Lines
#+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)))
(defalias 'start-lsp-server #'eglot)
(use-package breadcrumb
:config (breadcrumb-mode))
#+end_src
*** DISABLED lsp-bridge
#+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)
;; )
#+end_src
** Prevent breaking of hardlinks
#+begin_src emacs-lisp
(setq backup-by-copying-when-linked t)
#+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
#+begin_src emacs-lisp
(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)))
#+end_src
** pdf support
#+begin_src emacs-lisp
(use-package pdf-tools
:init
(if (not (boundp 'pdf-tools-directory))
(pdf-tools-install))
:mode ("\\.pdf" . pdf-view-mode))
#+end_src
** 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
#+begin_src emacs-lisp
(use-package ein)
#+end_src
* Applications
** Obsidian
#+begin_src emacs-lisp
;; (use-package obsidian
;; :ensure t
;; :demand t
;; :config
;; (obsidian-specify-path swarsel-obsidian-vault-directory)
;; (global-obsidian-mode t)
;; :custom
;; ;; This directory will be used for `obsidian-capture' if set.
;; (obsidian-inbox-directory "Inbox")
;; (bind-key (kbd "C-c M-o") 'obsidian-hydra/body 'obsidian-mode-map)
;; :bind (:map obsidian-mode-map
;; ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
;; ("C-c C-o" . obsidian-follow-link-at-point)
;; ;; Jump to backlinks
;; ("C-c C-b" . obsidian-backlink-jump)
;; ;; If you prefer you can use `obsidian-insert-link'
;; ("C-c C-l" . obsidian-insert-wikilink)))
#+end_src
** Anki
*** Basic Anki setup
#+begin_src emacs-lisp
;; (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)
#+end_src
*** Own Anki functions
- These functions enable you to quickly set the destination note type and deck
#+begin_src emacs-lisp
;; (defvar swarsel-anki-deck nil)
;; (defvar swarsel-anki-notetype nil)
;; (defvar swarsel-anki-fields nil)
;; (defun swarsel-anki-set-deck-and-notetype ()
;; (interactive)
;; (setq swarsel-anki-deck (completing-read "Choose a deck: "
;; (sort (anki-editor-deck-names) #'string-lessp)))
;; (setq swarsel-anki-notetype (completing-read "Choose a note type: "
;; (sort (anki-editor-note-types) #'string-lessp)))
;; (setq swarsel-anki-fields (progn
;; (anki-editor--anki-connect-invoke-result "modelFieldNames" `((modelName . ,swarsel-anki-notetype)))))
;; )
;; (defun swarsel-anki-make-template-string ()
;; (if (not swarsel-anki-deck)
;; (call-interactively 'swarsel-anki-set-deck-and-notetype))
;; (setq swarsel-temp swarsel-anki-fields)
;; (concat (concat "* %<%H:%M>\n:PROPERTIES:\n:ANKI_NOTE_TYPE: " swarsel-anki-notetype "\n:ANKI_DECK: " swarsel-anki-deck "\n:END:\n** ")(pop swarsel-temp) "\n%?\n** " (mapconcat 'identity swarsel-temp "\n\n** ") "\n\n"))
;; (defun swarsel-today()
;; (format-time-string "%Y-%m-%d"))
;; (defun swarsel-obsidian-daily ()
;; (interactive)
;; (if (not (file-exists-p (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
;; (write-region "" nil (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory))
;; )
;; (find-file (expand-file-name (concat (swarsel-today) ".md") swarsel-obsidian-daily-directory)))
#+end_src
* Email + Calendar
** make sure mu4e is found
#+begin_src emacs-lisp
(let ((mu4epath
(concat
(f-dirname
(file-truename
(executable-find "mu")))
"/../share/emacs/site-lisp/mu4e")))
(when (and
(string-prefix-p "/nix/store/" mu4epath)
(file-directory-p mu4epath))
(add-to-list 'load-path mu4epath)))
#+end_src
** mu4e
#+begin_src emacs-lisp
(use-package mu4e
:ensure nil
;; :load-path "/usr/share/emacs/site-lisp/mu4e/"
;;:defer 20 ; Wait until 20 seconds after startup
:config
;; This is set to 't' to avoid mail syncing issues when using mbsync
(setq send-mail-function 'sendmail-send-it)
(setq mu4e-change-filenames-when-moving t)
(setq mu4e-mu-binary (executable-find "mu"))
(setq mu4e-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-hide-index-messages t)
(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
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
#+begin_src emacs-lisp
(use-package mu4e-alert)
(mu4e-alert-set-default-style 'libnotify)
(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
(mu4e t)
#+end_src
** Calendar
#+begin_src emacs-lisp
(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"))))
#+end_src
* Startup Application Scripts
Yep, none currently.
* Emacs startup screen
#+begin_src emacs-lisp
;;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-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")))
)
)))
(setq dashboard-projects-switch-function 'project-switch-project)
#+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