#+title: My Emacs Config
#+author: William Ball
#+email: wball@ballcloud.cc
#+property: header-args:emacs-lisp :tangle ./init.el :mkdirp

* Basic Settings
** UI
#+begin_src emacs-lisp
  (setq inhibit-startup-message t)
  (tool-bar-mode -1)
  (tooltip-mode -1)
  (set-fringe-mode 10)
  (menu-bar-mode -1)
  (setq visible-bell t)
  (setq frame-resize-pixelwise t)
  (fset 'yes-or-no-p 'y-or-n-p)
  (setq default-frame-alist '((undecorated . t)))
  (scroll-bar-mode -1)

  ; get rid of that stupid popup
  (setq native-comp-async-report-warnings-errors 'silent)
#+end_src
** Backups
#+begin_src emacs-lisp
  (defvar wball/backup-directory (concat user-emacs-directory "backups"))
  (if (not (file-exists-p wball/backup-directory))
      (make-directory wball/backup-directory t))
  (setq backup-directory-alist `(("." . ,wball/backup-directory)))
  (setq make-backup-files t
        backup-by-copying t
        version-control t
        delete-old-versions t
        auto-save-default t)
#+end_src
* Packages
** MELPA
#+begin_src emacs-lisp
  (require 'package)
  (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
  (package-initialize)
#+end_src
** use-package
#+begin_src emacs-lisp
  (require 'use-package-ensure)
  (setq use-package-always-ensure t)
#+end_src
** no-littering
#+begin_src emacs-lisp
  (use-package no-littering)
#+end_src
** Set up path correctly
#+begin_src emacs-lisp
  (use-package exec-path-from-shell
    :config
    (exec-path-from-shell-initialize))
#+end_src
** Automatic updates
#+begin_src emacs-lisp
  (use-package auto-package-update
    :custom
    (auto-package-update-interval 7)
    (auto-package-update-prompt-before-update t)
    (auto-package-update-hide-results t)
    :config
    (auto-package-update-maybe)
    (auto-package-update-at-time "09:00"))
#+end_src
* UI
** Icons
#+begin_src emacs-lisp
  (use-package nerd-icons
    :custom
    (nerd-icons-font-family "Iosevka Nerd Font"))
#+end_src
** Rainbow delimiters
#+begin_src emacs-lisp
  (use-package rainbow-delimiters
    :hook (prog-mode . rainbow-delimiters-mode))
#+end_src
** Fonts
#+begin_src emacs-lisp
  (use-package fontaine
    :config
    (setq fontaine-presets
          '((regular
             :default-height 140)
            (small
             :default-height 90)
            (large
             :default-weight semilight
             :default-height 160
             :bold-weight extrabold)
            (extra-large
             :default-weight semilight
             :default-height 190
             :bold-weight extrabold)
            (t
             :default-family "Iosevka Nerd Font"
             :default-weight normal
             :variable-pitch-family "Libertinus Serif")))
    (fontaine-set-preset 'regular))
#+end_src
** Which key
#+begin_src emacs-lisp
  (use-package which-key
    :defer 0
    :diminish which-key-mode
    :config
    (which-key-mode)
    (setq which-key-idle-delay 0.3))
#+end_src
** Hide Modeline
#+begin_src emacs-lisp
  (use-package hide-mode-line)
#+end_src
** Theme
*** Modus Themes
#+begin_src emacs-lisp
  ;; (use-package emacs
  ;;   :ensure nil
  ;;   :config
  ;;   (require-theme 'modus-themes)
  ;;   (setq modus-themes-italic-constructs t
  ;;         modus-themes-bold-constructs t
  ;;    modus-themes-links '(no-underline background faint)
  ;;         modus-themes-prompts '(bold intense)
  ;;         modus-themes-mode-line '(borderless accented moody)
  ;;         modus-themes-org-blocks 'gray-background
  ;;         modus-themes-region '(bg-only no-extend))
  ;;   (load-theme 'modus-operandi :no-confirm))
#+end_src
*** Autodark
#+begin_src emacs-lisp
  ;; (use-package auto-dark
  ;;   :ensure t
  ;;   :custom
  ;;   (auto-dark-themes '((modus-vivendi) (modus-operandi)))
  ;;   :init (auto-dark-mode))
#+end_src
*** Catpuccin
#+begin_src emacs-lisp
  ;; (use-package catppuccin-theme
  ;;   :init (setq catppuccin-flavor 'mocha)
  ;;   :config
  ;;   (load-theme 'catppuccin :no-confirm))
#+end_src
*** Doom-themes
#+begin_src emacs-lisp
  (use-package doom-themes
    :config
    (load-theme 'doom-gruvbox t))
#+end_src
*** Solaire
#+begin_src emacs-lisp
  (use-package solaire-mode
    :config
    (solaire-global-mode))
#+end_src
** Ultra Scroll
#+begin_src emacs-lisp
  (use-package ultra-scroll
    :custom
    (scroll-conservatively 3)
    (scroll-margin 0)
    :config
    (ultra-scroll-mode 1))
#+end_src
* Functionality
** Navigation
*** Ace-Window
#+begin_src emacs-lisp
  (use-package ace-window
    :bind (("M-o" . ace-window)))
#+end_src
*** Avy
#+begin_src emacs-lisp
  (use-package avy
    :bind
    (("C-:" . avy-goto-char)
     ("C-M-'" . avy-goto-char-2)
     ("M-g w" . avy-goto-word-1)
     ("M-g W" . avy-goto-word-0)))
#+end_src
** Inhibit Mouse
I find I accidentally click with the trackpad on my laptop not infrequently, and basically never use it otherwise, so I might as well get rid of it.
#+begin_src emacs-lisp
  (use-package inhibit-mouse
    :custom
    (inhibit-mouse-adjust-mouse-highlight t)
    (inhibit-mouse-adjust-show-help-function t)
    :config
    (if (daemonp)
        (add-hook 'server-after-make-frame-hook #'inhibit-mouse-mode)
      (inhibit-mouse-mode 1)))
#+end_src

* Completion
** Vertico
#+begin_src emacs-lisp
  (use-package vertico
    :init (vertico-mode)
    :custom (vertico-cycle t))
#+end_src
** Savehist
#+begin_src emacs-lisp
  (use-package emacs
    :ensure nil
    :init (savehist-mode))
#+end_src
** Orderless
#+begin_src emacs-lisp
  (use-package orderless
    :init
    (setq completion-styles '(orderless flex)
          completion-category-defaults nil
          completion-category-overrides '((file (styles partial-completion)))))
#+end_src
** Corfu
#+begin_src emacs-lisp
  (use-package corfu
    :custom
    (corfu-cycle t)
    (corfu-auto nil)
    (corfu-echo-documentation 0.25)
    :init
    (global-corfu-mode)
    (setq tab-always-indent 'complete))
#+end_src
** Cape
#+begin_src emacs-lisp
  (use-package cape
    :init
    (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-history))
#+end_src
** Marginalia
#+begin_src emacs-lisp
  (use-package marginalia
    :after vertico
    :init (marginalia-mode))
#+end_src
** Consult
#+begin_src emacs-lisp
  (use-package consult
    :bind (("C-c M-x" . consult-mode-command)
           ("C-c h" . consult-history)
           ("C-c k" . consult-kmacro)
           ("C-c m" . consult-man)
           ("C-c i" . consult-info)
           ([remap Info-search] . consult-info)
           ;; C-x bindings in `ctl-x-map'
           ("C-x M-:" . consult-complex-command)
           ("C-x b" . consult-buffer)
           ("C-x 4 b" . consult-buffer-other-window)
           ("C-x 5 b" . consult-buffer-other-frame)
           ("C-x t b" . consult-buffer-other-tab)
           ("C-x r b" . consult-bookmark)
           ("C-x p b" . consult-project-buffer)
           ;; Custom M-# bindings for fast register a
           ("M-#" . consult-register-load)
           ("M-'" . consult-register-store)
           ("C-M-#" . consult-register)
           ;; Other custom bindings
           ("M-y" . consult-yank-pop)
           ;; M-g bindings in `goto-map'
           ("M-g e" . consult-compile-error)
           ("M-g f" . consult-flymake)
           ("M-g g" . consult-goto-line)
           ("M-g M-g" . consult-goto-line)
           ("M-g o" . consult-outline)
           ("M-g m" . consult-mark)
           ("M-g k" . consult-global-mark)
           ("M-g i" . consult-imenu)
           ("M-g I" . consult-imenu-multi)
           ;; M-s bindings in `search-map'
           ("M-s d" . consult-fd)
           ("M-s c" . consult-locate)
           ("M-s g" . consult-grep)
           ("M-s G" . consult-git-grep)
           ("M-s r" . consult-ripgrep)
           ("M-s l" . consult-line)
           ("M-s L" . consult-line-multi)
           ("M-s k" . consult-keep-lines)
           ("M-s u" . consult-focus-lines)
           ;; Isearch integration
           ("M-s e" . consult-isearch-history)
           :map isearch-mode-map
           ("M-e" . consult-isearch-history)
           ("M-s e" . consult-isearch-history)
           ("M-s l" . consult-line)
           ("M-s L" . consult-line-multi)
           ;; Minibuffer history
           :map minibuffer-local-map
           ("M-s" . consult-history)
           ("M-r" . consult-history))

    :hook (completion-list-mode . consult-preview-at-point-mode)

    :init
    (advice-add #'register-preview :override #'consult-register-window)
    (setq register-preview-delay 0.5)

    (setq xref-show-xrefs-function #'consult-xref
          xref-show-definitions-function #'consult-xref)

    :config

    (consult-customize
     consult-theme :preview-key '(:debounce 0.2 any)
     consult-ripgrep consult-git-grep consult-grep
     consult-bookmark consult-recent-file consult-xref
     consult--source-bookmark consult--source-file-register
     consult--source-recent-file consult--source-project-recent-file
     :preview-key '(:debounce 0.4 any))

    (setq consult-narrow-key "<"))
#+end_src
* Lsp
** eglot
#+begin_src emacs-lisp
  (use-package eglot
    :ensure nil
    :bind (:map eglot-mode-map
                ("C-c l r" . eglot-rename)
                ("C-c l f" . eglot-format-buffer)
                ("C-c l a" . eglot-code-actions)
                ("C-c l q" . eglot-code-action-quickfix)
                ("C-c l d" . xref-find-definitions)
                ("C-c l i" . eglot-find-implementation)
                ("C-c l t" . eglot-find-type-definition)
                ("C-c l s" . xref-find-references)
                ("C-c l h" . eldoc)
                ("C-c l R" . eglot-restart-process)
                ("C-c l e" . eglot-errors-at-point))
    (:map flymake-mode-map
        ("M-n" . flymake-goto-next-error)
        ("M-p" . flymake-goto-prev-error)))
#+end_src
** eldoc-box
#+begin_src emacs-lisp
  (use-package eldoc-box
    :hook (eglot-managed-mode . eldoc-box-hover-at-point-mode))
#+end_src
** Markdown
I don't really use markdown ([[Org Mode]] is a strict improvement imo), but it's handy for rendering documentation.
#+begin_src emacs-lisp
  (use-package markdown-mode)
#+end_src
* Languages
** Agda
#+begin_src emacs-lisp
  ;; (load-file (let ((coding-system-for-read 'utf-8))
  ;;                 (shell-command-to-string "agda-mode locate")))
#+end_src
** PG
#+begin_src emacs-lisp
  (use-package proof-general)
#+end_src
** Perga
#+begin_src emacs-lisp
  (use-package perga-mode
    :vc (:url "https://forgejo.ballcloud.cc/wball/perga-mode.git"
         :rev :latest))
#+end_src
** Scheme
#+begin_src emacs-lisp
  (use-package geiser)
  (use-package geiser-chez
    :custom
    (geiser-chez-binary "chez"))
#+end_src
** Haskell
#+begin_src emacs-lisp
  (use-package haskell-mode)
#+end_src
** SML
#+begin_src emacs-lisp
  (use-package sml-mode)
#+end_src
* Latex
** Auctex
#+begin_src emacs-lisp
  (use-package tex
    :ensure auctex
    :hook
    (LaTeX-mode . visual-line-mode)
    (LaTeX-mode . flyspell-mode)
    (LaTeX-mode . reftex-mode)
    (LaTeX-mode . prettify-symbols-mode)
    (LaTeX-mode . outline-minor-mode)
    :custom
    (TeX-auto-save t)
    (TeX-parse-self t)
    (TeX-master t)
    (reftex-plug-in-to-AUCTeX t)
    (preview-auto-cache-preamble t)
    (preview-scale-function 0.50))
#+end_src
** CDLatex
#+begin_src emacs-lisp
  (use-package cdlatex
    :hook
    (LaTeX-mode . #'turn-on-cdlatex)
    :custom
    (cdlatex-math-symbol-prefix ?\;))
#+end_src
** Yasnippet and laas
#+begin_src emacs-lisp
  (use-package yasnippet
    :config
    (yas-global-mode 1))
#+end_src

#+begin_src emacs-lisp
  (defun wball/beginning-of-word ()
    "Return t if point is at the beginning of a word."
    (eq (point) (car (bounds-of-thing-at-point 'word))))

  (use-package laas
    :hook ((LaTeX-mode . laas-mode)
           (org-mode . laas-mode))
    :custom
    (laas-enable-auto-space nil)
    :config
    (aas-set-snippets 'laas-mode
      :cond #'wball/beginning-of-word
      "mk" (lambda () (interactive)
             (yas-expand-snippet "\\\\($1\\\\)$0"))
      "dm" (lambda () (interactive)
             (yas-expand-snippet "\\[\n\t$1\n\\]"))
      :cond (lambda () (and (wball/beginning-of-word) (texmathp)))
      "set" (lambda () (interactive)
              (yas-expand-snippet "\\\\{$1\\\\}$0"))
      "empty" "\\emptyset"
      "and" "\\wedge"
      "or" "\\vee"
      "forall" "\\forall"
      "exists" "\\exists"
      "notin" "\\notin"
      "cap" "\\cap"
      "cup" "\\cup"
      :cond #'texmathp
      "NN" "\\N"
      "ZZ" "\\Z"
      "QQ" "\\Q"
      "rt" "\\upharpoonright"
      "bt" "\\bullet"
      "'d" "^\\bullet"
      "oo" "\\circ"
      "RR" "\\R"
      "CC" "\\C"
      "BB" "\\B"
      "PP" "\\P"
      "FF" "\\F"
      "HH" "\\H"
      "SS" "\\Ss"
      "TT" "\\T"
      "\\\\\\" "\\setminus"
      "s=" "\\subseteq"
      "s<" "\\subset"
      "invs" "^{-1}"
      "td" (lambda () (interactive)
             (yas-expand-snippet "^{$1}$0"))
      "__" (lambda () (interactive)
             (yas-expand-snippet "_{$1}$0"))))
#+end_src
* Org Mode
** Org itself
#+begin_src elisp
  (package-vc-install '(org-mode :url "https://code.tecosaur.net/tec/org-mode" :branch "dev"))
#+end_src
I use [[https://abode.karthinks.com/org-latex-preview/][this]] fork of org-mode in order to have amazing latex previews. /Make sure to follow the installation instructions!/ It's supposed to eventually be merged into org proper, which is very exciting. I'm already pretty dependent on this feature (the previews are just that good), so I'll have to use this until it gets merged, but that's fine with me.
#+begin_src emacs-lisp
  (use-package org-mode
    :vc (:url "https://code.tecosaur.net/tec/org-mode"
         :branch "dev")
    :load-path "~/dotfiles/emacs/.config/emacs/elpa/org-mode/lisp/"
    :init
    (setq org-list-allow-alphabetical t)
    :custom
    (org-log-into-drawer t)
    (org-startup-indented t)
    (org-hide-emphasis-markers t)
    (org-startup-with-inline-images t)
    (org-image-actual-width '(300))
    (org-export-with-smart-quotes t)
    (org-directory "~/Nextcloud/org")
    (org-cite-global-bibliography '("~/Nextcloud/Documents/library.bib"))
    (org-agenda-files `(,org-directory))
    (org-todo-keywords
     '((sequence "TODO" "VERIFY" "|" "DONE")))
    (org-default-notes-file (mapcar (lambda (x) (concat org-directory x))
                                    '("/todo.org" "/done.org")))
    (org-latex-preview-mode-display-live t)
    :config
    (plist-put org-latex-preview-appearance-options
               :page-width 0.8)
    (setq org-format-latex-options (plist-put org-format-latex-options :scale 0.6))
    (require 'ox-latex)
    (require 'ox-beamer)
    (require 'org-tempo)
    (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
    (add-to-list 'org-structure-template-alist '("ltx" . "export latex"))
    (add-to-list 'org-structure-template-alist '("pf" . "proof"))
    (add-to-list 'org-structure-template-alist '("thm" . "theorem"))
    (add-to-list 'org-structure-template-alist '("def" . "definition"))
    (add-to-list 'org-structure-template-alist '("lem" . "lemma"))
    :hook
    (org-mode . visual-line-mode)
    (org-mode . flyspell-mode)
    (org-mode . org-latex-preview-mode))
#+end_src
** Org Noter
#+begin_src emacs-lisp
  (use-package djvu)
  (use-package nov)
  (use-package org-noter)
#+end_src
** Olivetti
#+begin_src emacs-lisp
  (use-package olivetti
    :custom
    (fill-column 80)
    :hook
    (org-mode . olivetti-mode))
#+end_src
** Mixed Pitch
#+begin_src emacs-lisp
  (use-package mixed-pitch
    :hook
    (org-mode . mixed-pitch-mode))
#+end_src
** toki pona support
#+begin_src emacs-lisp
  (with-eval-after-load 'ox
    (add-to-list 'org-export-dictionary
                 '("Footnotes" ("tok" :default "toki anpa")))
    (add-to-list 'org-export-dictionary
                 '("Author" ("tok" :default "jan toki")))
    (add-to-list 'org-export-dictionary
                 '("Table of Contents" ("tok" :default "ni li lon lipu ni::")))
    (add-to-list 'org-export-dictionary
                 '("Created" ("tok" :default "mi pali e ni lon tenpo"))))
#+end_src
** Prettify
#+begin_src emacs-lisp
  (use-package org-modern
    :hook ((org-mode . org-modern-mode)
           (org-agenda-finalize . org-modern-agenda)))
#+end_src
** Simple Presentations
#+begin_src emacs-lisp
  (use-package org-present)
#+end_src
** Org Bloggy stuff
#+begin_src emacs-lisp
  (setq org-publish-project-alist
        `(("org-posts"
           :base-directory "~/repos/blog/posts/"
           :publishing-directory "/ssh:server:~/blog/"
           :base-extension "org"
           :recursive t
           :publishing-function org-html-publish-to-html
         :author "William Ball"
         :language "en"
           :auto-sitemap t
           :sitemap-filename "archive.org"
           :sitemap-title "Archive"
           :sitemap-style list
         :sitemap-sort-files anti-chronologically
           :html-head "<link rel=\"stylesheet\" href=\"/static/style.css\" type=\"text/css\"/><link rel=\"icon\" href=\"static/favicon.ico\"/>"
           :html-preamble t
           :html-preamble-format (("en" "<div class=\"blog-header\"><a href=\"archive.html\">Post Archive</a></div>")
                                  ("tok" "<div class=\"blog-header\"><a href=\"archive.html\">toki ante</a></div>"))
           )
        ("org-pages"
         :base-directory "~/repos/blog/pages/"
         :publishing-directory "/ssh:server:~/blog/"
         :base-extension "org"
         :recursive t
         :publishing-function org-html-publish-to-html
         :author "William Ball"
         :language "en"
         :html-head "<link rel=\"stylesheet\" href=\"/static/style.css\" type=\"text/css\"/><link rel=\"icon\" href\"static/favicon.ico\"/>"
         )
        ("org-static"
         :base-directory "~/repos/blog/static/"
         :publishing-directory "/ssh:server:~/blog/static/"
         :base-extension "ico\\|ttf\\|css"
         :publishing-function org-publish-attachment
         :recursive t
         )
        ("org-media"
         :base-directory "~/repos/blog/media/"
         :publishing-directory "/ssh:server:~/blog/media/"
         :base-extension "png\\|svg\\|pdf\\|mp4\\|mp3\\|ogg"
         :publishing-function org-publish-attachment
         :recursive t
         )
        ("org" :components ("org-posts" "org-static" "org-media" "org-pages"))
        ))

  (use-package htmlize)
#+end_src
** Simple http server
#+begin_src emacs-lisp
  (use-package simple-httpd)
#+end_src
* Pdfs
** Pdf tools
I'm running into weird performance issues with =pdf-tools=. I think it has to do with HiDPI. Pdfs are a little blurry without scaling set, but it's way better than without.
#+begin_src emacs-lisp
  (use-package pdf-tools
    :custom
    (pdf-view-use-scaling nil)
    :config
    (pdf-tools-install))
#+end_src
* Terminals/Shells
** Eshell
#+begin_src emacs-lisp
  (defun wball/configure-eshell ()
    (add-hook 'eshell-pre-command-hook 'eshell-save-some-history)
    (add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer)

    (setq eshell-history-size 10000
          eshell-buffer-maximum-lines 10000
          eshell-history-ignoredups t
          eshell-scroll-to-bottom-on-input t))

  (use-package eshell
    :ensure nil
    :commands eshell
    :hook (eshell-first-time-mode . wball/configure-eshell)
    :bind (("C-c e" . eshell))
    :config
    (with-eval-after-load 'esh-opt
      (setq eshell-destroy-buffer-when-process-dies t)))

  (defalias 'eshell/ff 'find-file)
  (defalias 'eshell/fo 'find-file-other-window)
#+end_src
** VTerm
#+begin_src emacs-lisp
  (use-package vterm)
#+end_src
** Dired
#+begin_src emacs-lisp
  (use-package emacs
    :ensure nil
    :commands (dired dired-jump)
    :custom ((dired-listing-switches "-agho --group-directories-first"))
    :config
    (require 'dired-x))

  (use-package dired-hide-dotfiles
    :hook (dired-mode . dired-hide-dotfiles-mode))
#+end_src
* Misc
** magit
#+begin_src emacs-lisp
  (use-package transient
    :ensure t)

  (use-package magit
    :after transient)
#+end_src
** Newsticker
#+begin_src emacs-lisp
  (use-package newsticker
    :ensure nil
    :custom
    (newsticker-url-list-defaults nil)
    (newsticker-url-list
     '(("Logic Arxiv" "https://rss.arxiv.org/rss/math.LO")
       ("Planet Emacslife" "https://planet.emacslife.com/atom.xml")
       ("It's Foss News" "https://news.itsfoss.com/latest/rss"))))
#+end_src
** Books
#+begin_src emacs-lisp
  (defun wball/books ()
  "Recursively search and open books from ~/Nextcloud/Books/."
  (interactive)
  (let* ((books-dir (expand-file-name "~/Nextcloud/Books/"))
         (files (directory-files-recursively books-dir "\\.pdf$\\|\\.epub$\\|\\.djvu$" nil))
         (relative-files (mapcar (lambda (f)
                                   (string-remove-prefix books-dir f))
                                 files))
         (choice (completing-read "Book: " relative-files nil t)))
    (when choice
      (find-file (concat books-dir choice)))))

(global-set-key (kbd "C-c b") 'wball/books)
#+end_src
