#+TITLE: Emacs config
#+AUTHOR: Erwin Boskma
#+OPTIONS: toc:nil h:4
#+STARTUP: overview
#+PROPERTY: header-args:emacs-lisp :tangle init.el
#+PROPERTY: header-args:emacs-lisp+ :noweb tangle

This is my emacs configuration. It is structured as an org-mode file, to easily provide context and documentation.

Last export: {{{modification-time(%Y-%m-%d %H:%M)}}}

#+TOC: headlines 4


* Setup
** Early init

This ends up in =early-init.el=.

#+begin_src emacs-lisp :tangle early-init.el
  ;; -*- lexical-binding: t; -*-
  ;; Prevent package.el from loading packages until we're ready for it
  (setq package-enable-at-startup nil)
  ;; Don't resize the frame to preserve the number of columns or lines
  ;; being displayed when setting font, menu bar, tool bar, tab bar,
  ;; internal borders, fringes, or scroll bars.  Since I use a tiling
  ;; window manager, this option is 1. useless anyways and 2. _terribly_ expensive.
  (setq frame-inhibit-implied-resize t)
  ;; Suppress warnings and errors during asynchronous native compilation
  (setq native-comp-async-report-warnings-errors nil)
  ;; Prevent unwanted runtime builds; packages are compiled ahead-of-time when
  ;; they are installed and site files are compiled when gccemacs is installed.
  ;; (setq comp-deferred-compilation nil)
  (setq native-comp-deferred-compilation nil)

  ;; Prevent unwanted runtime builds in gccemacs (native-comp); packages are
  ;; compiled ahead-of-time when they are installed and site files are compiled
  ;; when gccemacs is installed.
  (setq comp-deferred-compilation nil)

  ;; This should fix input lag by ~80% when using PGTK
  (setq-default pgtk-wait-for-event-timeout 0)
#+end_src

** Enable lexical binding

Setting =lexical-binding= to =t= can improve startup time. This has to be first!

#+begin_src emacs-lisp
  ;; -*- lexical-binding: t; -*-
#+end_src

** Personal variables

This sets some variables with my personal preferences for easy customization

#+begin_src emacs-lisp
  (defvar my/default-font "RecMonoLinear Nerd Font")
  (defvar my/variable-width-font "Iosevka Aile")
  (defvar my/comment-font "RecMonoCasual Nerd Font")
  (defvar my/default-font-height 120)
  (defvar my/default-font-weight 'light)
  (defvar my/default-font-width 'normal)
  (defvar my/variable-width-font-height 1.1)
  (defvar my/config-file-path (expand-file-name "config.org" user-emacs-directory))
  (defvar my/snippets-dir (expand-file-name "snippets" user-emacs-directory))
  (defvar my/local-init-file (expand-file-name "local-init.el" user-emacs-directory))
  (defvar my/leader "C-c")
#+end_src

And my personal info.

#+begin_src emacs-lisp
  (setq user-full-name "Erwin Boskma"
        user-mail-address "erwin@datarift.nl")
#+end_src

** Garbage collector
Increasing the garbage collector threshold should also improve startup time. This increases it from 800 kB to 128MB

#+begin_src emacs-lisp
  (setq gc-cons-threshold (* 128 1024 1024))
#+end_src

This resets the threshold back to it's default. This should not be done if =lsp-mode= is enabled, it needs the higher threshold.

#+begin_src emacs-lisp :tangle no
  (add-hook 'after-init-hook
            (lambda ()
              (setq gc-cons-threshold 800000)
              (message "gc-cons-threshold restored to %s"
                       gc-cons-threshold)))
#+end_src

** Increase process output buffer

LSP responses can be rather large, in the 800KiB - 3MiB range. 2MiB is a decent value

#+begin_src emacs-lisp
  (setq read-process-output-max (* 2 1024 1024))
#+end_src

** Package sources

Add repositories where packages are installed from.

#+begin_src emacs-lisp
    (setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
                             ("nongnu" . "https://elpa.nongnu.org/nongnu/")
                             ("melpa" . "https://melpa.org/packages/")
                             ("org" . "https://orgmode.org/elpa/")
                             ("onpa" . "https://olanilsson.bitbucket.io/packages/")))
#+end_src

** Bootstrap use-package

If =use-package= is not installed, install it.

*NOTE*: Disabled because it is builtin since emacs 29

#+begin_src emacs-lisp :tangle no
  (require 'package)
  (package-initialize)
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package)
    (cl-eval-when 'compile (require 'use-package)))
#+end_src

By default packages should always be installed from the package manager. This is equivalent to setting =:ensure t= on each call to =use-package=. To override this for a package (e.g. because it is builtin, or a subpackage), use =:ensure nil=. This is done automatically for packages that use =:load-path=.

#+begin_src emacs-lisp
  (setq use-package-always-ensure t)
#+end_src

** general.el

[[https://github.com/noctuid/general.el][general.el]] provides a more convenient way for binding keys in emacs. It also integrates with =use-package= with the =:general= keyword.

#+begin_src emacs-lisp
  (use-package general
    :demand t
    :config
    (progn
      (general-define-key
       :prefix my/leader
       "a" 'org-agenda
       "k" 'general-describe-keybindings)
      (general-define-key
       "C-M-z" 'zap-to-char
       "M-z" 'zap-up-to-char)))

#+end_src

** Disable the customize interface

The =customize= functionality is annoying and messes up regularly. Stuff it has done so far:

- Clobber the path to =mix= in Elixir projects

This has been borrowed from [[https://github.com/doomemacs/doomemacs/blob/35865ef5e89442e3809b8095199977053dd4210f/core/core-ui.el#L628-L639][Doom Emacs]]
#+begin_src emacs-lisp
  (dolist (sym '(customize-option customize-browse customize-group customize-face
                                  customize-rogue customize-saved customize-apropos
                                  customize-changed customize-unsaved customize-variable
                                  customize-set-value customize-customized customize-set-variable
                                  customize-apropos-faces customize-save-variable
                                  customize-apropos-groups customize-apropos-options
                                  customize-changed-options customize-save-customized))
    (put sym 'disabled (concat "This config doesn't support `customize', configure Emacs from " user-emacs-directory "/config.org instead")))
  (put 'customize-themes 'disabled (concat "Use `load-theme' in " user-emacs-directory "/config.org instead"))
#+end_src

** Custom packages location

Emacs only includes files directly under =user-emacs-directory=. I like to keep custom packages in a separate =elisp/= subdirectory.

#+begin_src emacs-lisp
  (add-to-list 'load-path (expand-file-name "elisp/" user-emacs-directory))
#+end_src

** Record key frequency

Records what keys are used the most, so I can see if I can optimise shortcuts

#+begin_src emacs-lisp
  (use-package keyfreq
    :config
    (keyfreq-mode 1)
    (keyfreq-autosave-mode 1))
#+end_src

** Save minibuffer history

#+begin_src emacs-lisp
  (use-package savehist
    :ensure nil
    :init
    (savehist-mode))
#+end_src

* Preferences

Don't display the help screen at startup

#+begin_src emacs-lisp
  (setq inhibit-startup-screen t)
#+end_src

Enable line wrapping

#+begin_src emacs-lisp
  (global-visual-line-mode 1)
#+end_src

Disable title bar, toolbar and scroll bar

#+begin_src emacs-lisp
  ; This needs to go before scroll-bar-mode
  (setq-default default-frame-alist '((undecorated . t)))
  (tool-bar-mode -1)
  (menu-bar-mode -1)
  (scroll-bar-mode -1)
#+end_src

Don't show the warnings buffer for anything lesser than an error.

#+begin_src emacs-lisp
  (setq warning-minimum-level :error)
#+end_src

Keep buffers up-to-date automatically

#+begin_src emacs-lisp
  (global-auto-revert-mode t)
#+end_src

Automatically scroll the compile buffer

#+begin_src emacs-lisp
  (setq compilation-scroll-output 'first-error)
#+end_src

Always display line numbers, and the relative line number

#+begin_src emacs-lisp
  (setq display-line-numbers-type 'relative)
  (global-display-line-numbers-mode)
#+end_src

Show matching parenthesese

#+begin_src emacs-lisp
  (show-paren-mode 1)
  (setq show-paren-style 'expression)
#+end_src

Surround marked text with (), [] or {}

#+begin_src emacs-lisp
  (electric-pair-mode 1)
#+end_src

Centralise backup files (the =filename~= files), so they don't pollute the project.

#+begin_src emacs-lisp
  (setq backup-directory-alist '((".*" . "~/.local/share/emacs/backup"))
        backup-by-copying t   ; Don't delink hardlinks
        version-control t     ; Use version numbers on backup files
        delete-old-versions t ; Automatically delete obsolete backups
        kept-new-versions 20  ; How many of the newest versions to keep
        kept-old-versions 5   ; And how many of the oldest
        )
#+end_src

Same for auto save files (the ones with the =#=)

#+begin_src emacs-lisp
  (setq auto-save-file-name-transforms '((".*" "~/.local/share/emacs/autosave/" t)))
#+end_src

Default indenting to spaces. If tabs are necessary, this can be set buffer-local. A single tab character can be added using =C-q TAB=

#+begin_src emacs-lisp
  (setq-default indent-tabs-mode nil)
#+end_src

Show trailing whitespace

#+begin_src emacs-lisp
  (setq show-trailing-whitespace t)
#+end_src

Delete trailing whitespace on save.

#+begin_src emacs-lisp
  (add-hook 'before-save-hook 'delete-trailing-whitespace)
#+end_src

Sentences end in one space, not two. We're not using typewriters.

#+begin_src emacs-lisp
  (setq sentence-end-double-space nil)
#+end_src

Don't move files to trash when deleting.

#+begin_src emacs-lisp
  (setq delete-by-moving-to-trash nil)
#+end_src

Restore cursor position when re-opening a file

#+begin_src emacs-lisp
  (save-place-mode t)
#+end_src

Prefer to open frames in a horizontal split and make sure they're of a decent width

#+begin_src emacs-lisp
  (setq split-height-threshold nil
        window-min-width 100)
#+end_src

Set fill column to 80

#+begin_src emacs-lisp
  (setq-default fill-column 80)
#+end_src

Kill whole lines instead of clearing them

#+begin_src emacs-lisp
  (setq kill-whole-line t)
#+end_src

* Interface
** Easy edit config file

Disabled because the configuration is handled by Nix using [[https://github.com/nix-community/emacs-overlay][emacs-overlay]]

#+begin_src emacs-lisp :tangle no
  (defun find-config ()
    "Edit config.org"
    (interactive)
    (find-file my/config-file-path))

  (general-define-key
       :prefix my/leader
       "c" 'find-config)
#+end_src
** Appearance

Enable pixel scrolling.

#+begin_src emacs-lisp
  (setq pixel-scroll-precision-mode t)
#+end_src

I like the [[https://draculatheme.com][dracula theme]]

#+begin_src emacs-lisp :tangle no
  (use-package dracula-theme
    :init
    (load-theme 'dracula :no-confirm))
#+end_src

[[https://github.com/belak/emacs-monokai-pro-theme][Monokai Pro]] is also pretty.

#+begin_src emacs-lisp :tangle no
  (use-package monokai-pro-theme
    :init
    (load-theme 'monokai-pro-spectrum :no-confirm))
#+end_src

So is [[https://github.com/catppuccin/emacs][Catppuccin]]

#+begin_src emacs-lisp
    (use-package catppuccin-theme
      :init
      (setq catppuccin-flavor 'mocha)
      (load-theme 'catppuccin :no-confirm))
#+end_src

Set fonts.

#+begin_src emacs-lisp
  (defun my/set-font-size (&optional frame)
    (let* ((frame (or frame (selected-frame)))
           (geometry (frame-monitor-attribute 'geometry frame))
           (mm-size (frame-monitor-attribute 'mm-size frame))
           (width-px (caddr geometry))
           (width-mm (car mm-size))
           (width-in (/ width-mm 25.4))
           (display-dpi (/ width-px width-in))
           (font-height (cond
                         ((< display-dpi 110) 120)
                         ((< display-dpi 130) 140)
                         ((< display-dpi 160) 160)
                         (t 160))))
      (set-face-attribute 'default frame :height font-height)))

  (add-hook 'server-after-make-frame-hook 'my/set-font-size)
  (add-hook 'after-make-frame-functions 'my/set-font-size)

  (defun my/setup-fonts ()
    (set-face-attribute 'default nil :family my/default-font :weight 'light)
    (set-face-attribute 'font-lock-comment-face nil :font my/comment-font)
    (set-face-attribute 'variable-pitch nil
                        :font my/variable-width-font
                        :height my/variable-width-font-height))
  (add-hook 'after-init-hook 'my/setup-fonts)
#+end_src

Emoji support

#+begin_src emacs-lisp
    (defun set-emoji-font ()
      (when (member "Twitter Color Emoji" (font-family-list))
        (set-fontset-font t 'emoji (font-spec :family "Twitter Color Emoji") nil 'prepend)))

    (use-package emojify
      :defer t
      :init
      (setq emojify-display-style 'unicode)
      :config
      (set-emoji-font)
      :hook (server-after-make-frame . set-emoji-font))
#+end_src

Enable ligatures

#+begin_src emacs-lisp
  (setq iosevka-ligatures '("<---" "<--"  "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "<!--"
                                         "<==" "<===" "<=" "=>" "=>>" "==>" "===>" ">=" "<=>" "<==>" "<===>" "<====>" "<!---"
                                         "<~~" "<~" "~>" "~~>" "::" ":::" "==" "!=" "===" "!=="
                                         ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "=:" "<******>" "++" "+++"))
  (setq monaspace-ligatures '(
                                          ; ss01
                              "==" "===" "=/=" "!=" "!==" "/=" "/==" "~~" "=~" "!~"
                                          ; ss02
                              ">=" "<="
                                          ; ss03
                              "->" "<-" "=>" "<!--" "-->" "<~" "<~~" "~>" "~~>" "<~>"
                                          ; ss04
                              "</" "/>" "</>" "/\\" "\\/"
                                          ; ss05
                              "|>" "<|"
                                          ; ss06
                              "##" "###"
                                          ; ss07
                              "***" "/*" "*/" "/*/" "(*" "*)" "(*)"
                                          ; ss08
                              ".=" ".-" "..<"
                                          ; dlig & calt
                              "<!" "**" "::" "=:" "=!" "=/" "--" ".." "//" "&&" "||" ":=" ":>" ":<" "!!" ">:" "<:" "#=" "?:" "?." "??" ";;" "///" ":::" "..." "=!=" "=:=" "..=" "..-"))

  (use-package ligature
    :config
    ;; Enable all Iosevka ligatures in programming modes
    (ligature-set-ligatures 'prog-mode iosevka-ligatures)
    ;; Ligatures for Monaspace
    ;; Enables ligature checks globally in all buffers. You can also do it
    ;; per mode with `ligature-mode'.
    (global-ligature-mode t))
#+end_src

Add a dashboard on startup

#+begin_src emacs-lisp
  (use-package dashboard
    :after all-the-icons
    :hook (dashboard-mode . (lambda ()
                              (setq show-trailing-whitespace nil)))
    :config
    (setq dashboard-set-navigator t
          dashboard-center-content t
          dashboard-set-file-icons t
          dashboard-set-heading-icons t
          dashboard-set-init-info t
          dashboard-image-banner-max-height 250
          dashboard-banner-logo-title "Bûter, brea en griene tsiis, wa dat net sizze kin is gjin oprjochte Fries."
          dashboard-startup-banner (concat user-emacs-directory "images/ue-colorful.png")
          dashboard-footer-icon (all-the-icons-octicon "dashboard"
                                                 :height 1.1
                                                 :v-adjust -0.05
                                                 :face 'font-lock-keyword-face))
    (setq dashboard-navigator-buttons
          `(((,(all-the-icons-octicon "search" :height 0.9 :v-adjust -0.1)
               " Find file" nil
               (lambda (&rest _) (find-file)) nil "" "            C-x C-f"))
            ((,(all-the-icons-octicon "file-directory" :height 1.0 :v-adjust -0.1)
               " Open project" nil
               (lambda (&rest _) (projectile-switch-project)) nil "" "         C-c p p"))
            ((,(all-the-icons-octicon "three-bars" :height 1.1 :v-adjust -0.1)
               " File explorer" nil
               (lambda (&rest _) (projectile-dired)) nil "" "        C-c p D"))
            ((,(all-the-icons-octicon "settings" :height 0.9 :v-adjust -0.1)
               " Open settings" nil
               (lambda (&rest _) (find-config)) nil "" "        C-c C  "))))
    (setq
     dashboard-projects-backend 'projectile
     dashboard-items '((recents . 5)
                       (projects . 5)
                       (registers . 5)))
    (dashboard-setup-startup-hook)
    ;; Also show dashboard on new emacsclient window
    (setq initial-buffer-choice (lambda ()
                                  (get-buffer-create "*dashboard*")))
    :custom-face
    (dashboard-heading ((t (:weight bold)))))

#+end_src

** Modeline

[[https://github.com/myrjola/diminish.el][diminish]] hides modes from the modeline

#+begin_src emacs-lisp
  (use-package diminish)
#+end_src

[[https://github.com/domtronn/all-the-icons.el][all-the-icons]] provides ALL the icons. Run `all-the-icons-install-fonts` after installing to download the actual font.

#+begin_src emacs-lisp
  (use-package all-the-icons
    :if (display-graphic-p))
#+end_src

[[https://github.com/tarsius/minions][minions]] adds a menu for minor modes to the modeline

#+begin_src emacs-lisp
  (use-package minions
    :config
    (minions-mode 1))
#+end_src

Use [[https://github.com/seagle0128/doom-modeline][doom-modeline]] for a nice and fancy modeline
*2023-05-12* Disabled because it causes emacs to hang

#+begin_src emacs-lisp :tangle no
  (use-package doom-modeline
    :init
    (doom-modeline-mode 1)
    :config
    (setq doom-modeline-height 25
          doom-modeline-bar-width 6
          doom-modeline-lsp t
          doom-modeline-github nil
          doom-modeline-mu4e nil
          doom-modeline-irc nil
          doom-modeline-minor-modes t
          doom-modeline-persp-name nil
          doom-modeline-buffer-file-name-style 'truncate-except-project
          doom-modeline-icon t)
    :custom-face
    (mode-line ((t (:height 0.85))))
    (mode-line-inactive ((t (:height 0.85)))))
#+end_src

[[https://github.com/tarsius/moody][moody]] cleans up the modeline a bit so it is nicer to look at

#+begin_src emacs-lisp
  (use-package moody
    :config
    (setq x-underline-at-descent-line t)
    (moody-replace-mode-line-buffer-identification)
    (moody-replace-vc-mode)
    (moody-replace-eldoc-minibuffer-message-function))
#+end_src

** Command completion

*** Ivy / Counsel

*Disabled in favor of [[https://github.com/minad/vertico][vertico]]*

Ivy is a generic completion framework which uses the minibuffer.
[[https://github.com/abo-abo/swiper][ivy GitHub page]]

#+begin_src emacs-lisp :tangle no
  (use-package ivy
    :diminish t
    :bind
    <<ivy-binds>>
    :config
    <<ivy-config>>
    )
#+end_src

Ivy config

#+name: ivy-config
#+begin_src emacs-lisp :tangle no
    (ivy-mode t)
    (setq ivy-initial-inputs-alist nil
          ivy-use-virtual-buffers nil)
    (define-key read-expression-map (kbd "C-r") 'counsel-expression-history)
#+end_src

Ivy keybindings

#+name: ivy-binds
#+begin_src emacs-lisp :tangle no
  ("C-x s" . swiper)
  ("C-x C-r" . ivy-resume)
#+end_src

Counsel enhances emacs commands with ivy.
[[https://github.com/abo-abo/swiper][counsel GitHub page]]

#+begin_src emacs-lisp :tangle no
  (use-package counsel
    :bind
    ("M-x" . counsel-M-x)
    ("C-x C-m" . counsel-M-x)
    ("C-x C-f" . counsel-find-file)
    ("C-x c k" . counsel-yank-pop))
#+end_src

Company provides completion in the main buffer.
[[https://company-mode.github.io/][company website]]

#+begin_src emacs-lisp :tangle no
  (use-package company
    :diminish t
    :hook
    (after-init . global-company-mode))
#+end_src

Hydra allows you to group commands behind a custom prefix.
[[https://github.com/abo-abo/hydra][hydra GitHub page]]

#+begin_src emacs-lisp :tangle no
  (use-package ivy-hydra)
#+end_src

=major-mode-hydra= binds a single key to open a context sensitive hydra based on the current major mode. Hydras can be defined in =use-package= definitions via the =:mode-hydra= integration.

#+begin_src emacs-lisp :tangle no
  (use-package major-mode-hydra
    :bind
    ("C-M-SPC" . major-mode-hydra)
    :config
    (major-mode-hydra-define org-mode
                             ()
                             ("Tools"
                              (("l" org-lint "lint")))))
#+end_src

*** Vertico / Corfu

[[https://github.com/minad/vertico][vertico]] is a minimalistic completion UI based on the default completion system integrated in emacs.

#+begin_src emacs-lisp
  (use-package vertico
    :custom
    (vertico-cycle t)
    :init
    (vertico-mode))
#+end_src

[[https://github.com/minad/marginalia][marginalia]] adds extra information to the minibuffer completions.

#+begin_src emacs-lisp
  (use-package marginalia
    :after vertico
    :custom
    (marginalia-annotaters '(marginalia-annotaters-heavy marginalia-annotaters-light nil))
    :init
    (marginalia-mode))
#+end_src

[[https://github.com/minad/consult][consult]] is an alternative to counsel for ivy, but for vertico.

#+begin_src emacs-lisp
  (use-package consult
    :general
    ("C-s" 'consult-line)
    ("C-x c k" 'consult-yank-pop)
    :config
    (setq consult-project-function (lambda ()
                                     (when (fboundp 'projectile-project-root)
                                       (projectile-project-root)))))
#+end_src

[[https://github.com/minad/corfu][corfu]] enhances the completion at point function popups

#+begin_src emacs-lisp
      (use-package corfu
        ;; :bind
        ;; ("TAB" . corfu-insert)
        :custom
        (corfu-cycle t)
        (corfu-auto t)
        :init
        (global-corfu-mode))
#+end_src

[[https://github.com/oantolin/orderless][orderless]] is a flexible completion style that allows flexible matching using literal strings, regex and more.

#+begin_src emacs-lisp
  (use-package orderless
    :init
    (setq completion-styles '(orderless partial-completion basic)
          ;; completion-category-defaults nil
          completion-category-overrides '((file (styles basic partial-completion)))))
#+end_src

Use =corfu= with =dabbrev= (included with emacs)

#+begin_src emacs-lisp
  (use-package dabbrev
    :ensure nil
    :general
    ("M-/" 'dabbrev-completion)
    ("C-M-/" 'dabbrev-expand)
    :custom
    (dabbrev-ignored-buffer-regexps '("\\.\\(?:pdf\\|jpe?g\\|png\\)\\'")))
#+end_src

*** Misc
which-key shows suggestions when you type an incomplete command and wait for a bit (1 second by default).
[[https://github.com/justbur/emacs-which-key][which-key GitHub page]]

#+begin_src emacs-lisp
  (use-package which-key
    :defer 0
    :diminish which-key-mode
    :config
    (which-key-mode)
    (setq which-key-idle-delay 1))
#+end_src


*** Possible candidates

The following packages look interesting, and are worth investigating further:

- [[https://github.com/abo-abo/avy][avy]]: quick text navigation
- [[https://github.com/abo-abo/ace-window][ace-window]]: window navigation
- [[https://github.com/magnars/expang-region.el][expand-region]]: context aware text selection
- [[https://github.com/minad/cape][cape]]: A set of completion at point extensions

** File tree

[[https://github.com/Alexander-Miller/treemacs][treemacs]] is Emacs' equivalent of neovim's NeoTree or vim's NERDTree.

#+begin_src emacs-lisp
  (use-package treemacs
    :defer t
    :init
    (with-eval-after-load 'winum
      (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
    :general
    ("M-0" 'treemacs-select-window)
    (:prefix "C-c"
     "t 1"   'treemacs-delete-other-windows
     "t t"   'treemacs
     "t d"   'treemacs-select-directory
     "t B"   'treemacs-bookmark
     "t C-t" 'treemacs-find-file
     "t M-t" 'treemacs-find-tag)
    :config
    (progn
      (treemacs-project-follow-mode)))
#+end_src

Enable integration with =projectile=

#+begin_src emacs-lisp
  (use-package treemacs-projectile
    :after (treemacs projectile))
#+end_src

Add icons

#+begin_src emacs-lisp
  (use-package treemacs-icons-dired
    :hook (dired-mode . treemacs-icons-dired-enable-once))
#+end_src

Enable =magit= integration

#+begin_src emacs-lisp
  (use-package treemacs-magit
    :after (treemacs magit))
#+end_src

* Programming

** General settings

Auto balance parenthesese. *Note:* Disabled in favour of =electric-pair-mode=

#+begin_src emacs-lisp :tangle no
  (use-package smartparens
    :ghook 'prog-mode-hook)
#+end_src

Rainbow delimiters FTW!

#+begin_src emacs-lisp
  (use-package rainbow-delimiters
    :ghook 'prog-mode-hook)
#+END_src

Paredit is a minor mode for editing parentheses

#+begin_src emacs-lisp
  (use-package paredit
    :hook (emacs-lisp-mode . paredit-mode)
    (lisp-mode . paredit-mode)
    (racket-mode . paredit-mode)
    (racket-repl-mode . paredit-mode)
    :general
    (:keymaps 'paredit-mode-map
              "{" 'paredit-open-curly
              "}" 'paredit-close-curly
              "M-[" 'paredit-wrap-square
              "M-{" 'paredit-wrap-curly))
#+end_src

** Project management

Projectile works great to manage projects. It includes fuzzy search and supports jumping between files (e.g. .h <-> .cpp).
[[https://github.com/bbatsov/projectile][projectile GitHub page]]

#+begin_src emacs-lisp
  (use-package projectile
    :diminish projectile-mode
    :config
    ;; (setq projectile-completion-system 'ivy)
    (projectile-mode)
    :general
    (:prefix "C-c"
     "p" '(:keymap projectile-command-map))
    :init
    (setq projectile-switch-project-action #'projectile-dired
          projectile-project-search-path '("~/workspace" "~/workspace/horus" "~/workspace/horus/web" "~/workspace/horus/horus-vr")))
#+end_src

There is also an integration with counsel.

#+begin_src emacs-lisp :tangle no
  (use-package counsel-projectile
    :after projectile
    :general
    ("C-SPC" 'counsel-projectile-switch-project)
    :config
    (counsel-projectile-mode))
#+end_src

Enable [[https://github.com/BurntSushi/ripgrep][ripgrep]] support

#+begin_src emacs-lisp
  (use-package rg
    :after projectile
    :config
    (rg-enable-default-bindings))
#+end_src

** TRAMP

Set some connection properties

#+begin_src emacs-lisp
  (with-eval-after-load "tramp" (add-to-list 'tramp-connection-properties
                                             (list (regexp-quote "/sshx:hass:")
                                                   "remote-shell" "/bin/bash")))
#+end_src

** Ollama

Let's evaluate this puppy.

#+begin_src emacs-lisp
      (use-package gptel
        :general
        (:prefix my/leader
                 "m" 'gptel-menu
                 "C-m" 'gptel-send)
        :config
        (setq gptel-model "mistral-nemo"
              gptel-backend (gptel-make-ollama "Ollama"
                                               :host "100.119.162.110:11434"
                                               :stream t
                                               :models '("mistral"))))
  #+end_src
  
** Git

Magit. Obviously.
[[https://magit.vc][magit website]]

#+begin_src emacs-lisp
  (use-package magit
    :config
    ;; (setq magit-completing-read-function 'ivy-completing-read)
    :general
    (:prefix my/leader
     "g s" 'magit-status
     "g x" 'magit-checkout
     "g c" 'magit-commit
     "g p" 'magit-push
     "g u" 'magit-pull
     "g e" 'magit-ediff-resolve
     "g r" 'magit-rebase-interactive
     "g i" 'magit-init))
#+end_src

transient can be used to create command dispatchers. Magit uses it to easily add options to git commands, and it displays a nice popup with the possible flags and commands.
[[https://magit.vc/manual/transient/][transient manual]]

#+begin_src emacs-lisp
  (use-package transient)
#+end_src

Visualise git changes in the gutter, next to the line numbers
[[https://github.com/emacsorphanage/git-gutter][git-gutter GitHub page]]

#+begin_src emacs-lisp
  (use-package git-gutter
    :diminish t
    :ghook
    'prog-mode-hook
    'org-mode-hook
    :config
    (setq git-gutter:update-interval 0.02))

  (use-package git-gutter-fringe
    :config
    (define-fringe-bitmap 'git-gutter-fr:added [224] nil nil '(center repeated))
    (define-fringe-bitmap 'git-gutter-fr:modified [224] nil nil '(center repeated))
    (define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240] nil nil 'bottom))
#+end_src

Show inline git-blame with [[https://github.com/Artawower/blamer.el][blamer.el]]. Inspired by VS Code's Git Lens

#+begin_src emacs-lisp
  (use-package blamer
    :after bind-key
    :bind (("C-c C-i" . blamer-show-commit-info)
           ("C-c i" . blamer-show-posframe-commit-info))
    :config
    (global-blamer-mode 1))
#+end_src

** Syntax checking and highlighting

*** Flycheck

=Flycheck= is a general purpose syntax highlighting framework that provides hooks for other packages and an improvement of the builtin =flymake=.
[[https://www.flycheck.org][website]]

#+begin_src emacs-lisp
  (use-package flycheck
    :diminish t
    :init
    (global-flycheck-mode))
#+end_src

Add eglot support for flycheck-mode

#+begin_src emacs-lisp
  (use-package flycheck-eglot
    :after (flycheck eglot)
    :config
    (global-flycheck-eglot-mode 1))
#+end_src

*** Tree-sitter

[[https://tree-sitter.github.io/][tree-sitter]] is a new development in parsing and syntax highlighting. It has been merged into Emacs 29, but until that's released we're using the [[https://emacs-tree-sitter.github.io/][emacs-tree-sitter]] package while on Emacs 28.

#+begin_src emacs-lisp
  (when (< emacs-major-version 29)
    (use-package tree-sitter
    :config
    (global-tree-sitter-mode)
    :ghook
    ('tree-sitter-after-on-hook #'tree-sitter-hl-mode)))
#+end_src

[[https://github.com/emacs-tree-sitter/tree-sitter-langs][tree-sitter-langs]] provides =tree-sitter= support for a bunch of languages.

#+begin_src emacs-lisp
  (use-package tree-sitter-langs
    :after tree-sitter)
#+end_src

Automatically use the =<lang>-ts-mode= when it is available

#+begin_src emacs-lisp
  (use-package treesit-auto
    :config
    (setq treesit-auto-install 'prompt)
    (global-treesit-auto-mode))
#+end_src

*** eglot

[[https://joaotavora.github.io/eglot/][eglot]] is an alternative to =lsp-mode= that is builtin with emacs >= 29

#+begin_src emacs-lisp
  (use-package eglot
    :config
    (fset #'json--log-event #'ignore) ;; Performance boost by not logging every event
    (add-to-list 'eglot-server-programs
                 '((toml-mode toml-ts-mode conf-toml-mode) . ("taplo" "lsp" "stdio")))
    ;; (add-to-list 'eglot-server-programs
    ;;              `((elixir-mode elixir-ts-mode heex-ts-mode) .
    ;;                ,(eglot-alternatives '(("nextls" "--stdio=true"
    ;;                                        :initializationOptions (:experimental (:completions (:enable t))))
    ;;                                       "elixir-ls"))))
    ;;
    (add-to-list 'eglot-server-programs
                 `((elixir-ts-mode heex-ts-mode) .
                   ,(eglot-alternatives '("lexical" "elixir-ls"))))
    (add-to-list 'eglot-server-programs
                 '(dhall-mode . ("dhall-lsp-server")))
    (add-to-list 'eglot-stay-out-of 'flymake)
    (setq eglot-autoshutdown t
          eldoc-echo-area-use-multiline-p 0.1)
    :hook
    (eglot-managed-mode . (lambda ()
                            (eglot-inlay-hints-mode 1)
                            (define-key eglot-mode-map (kbd "C-c l a") 'eglot-code-actions)
                            (define-key eglot-mode-map (kbd "C-c l f") 'eglot-format)
                            (define-key eglot-mode-map (kbd "C-c l h") 'eldoc)
                            (define-key eglot-mode-map (kbd "C-c l i") 'eglot-find-implementation)
                            (define-key eglot-mode-map (kbd "C-c l r") 'eglot-rename)
                            (define-key eglot-mode-map (kbd "C-c l t") 'eglot-find-typeDefinition)
                            (define-key eglot-mode-map (kbd "C-c l w d") 'eglot-list-connections)
                            (define-key eglot-mode-map (kbd "C-c l w r") 'eglot-reconnect)
                            (define-key eglot-mode-map (kbd "C-c l w q") 'eglot-shutdown)
                            (define-key eglot-mode-map (kbd "C-c l y") 'eglot-inlay-hints-mode))))
#+end_src

[[https://github.com/nemethf/eglot-x][eglot-x]] adds support for some LSP extensions to =eglot=

#+begin_src emacs-lisp :tangle no
  (use-package eglot-x
    :vc (:fetcher github :repo nemethf/eglot-x)
    :after eglot
    :config
    (eglot-x-setup))
#+end_src

[[https://github.com/mohkale/consult-eglot][consult-eglot]] adds an integration between =consult= and =eglot=

#+begin_src emacs-lisp
  (use-package consult-eglot)
#+end_src

** Snippets

Snippets are predefined pieces of code that can be inserted and filled in. [[https://github.com/joaotavora/yasnippet][YASnippet]] uses syntax inspired by TextMate is the most popular, for good reason.

#+begin_src emacs-lisp
  (use-package yasnippet
    :init
    (load "yasnippet.el")
    :diminish t
    :general
    (:keymaps 'yas-minor-mode-map
              "<tab>" nil
              "TAB" nil
              "<C-tab>" 'yas-expand)
    :config
    (add-to-list 'yas-snippet-dirs my/snippets-dir)
    (yas-global-mode))

  (use-package yasnippet-snippets)
#+end_src

** Languages

*** JavaScript / TypeScript

Indent 2 spaces

#+begin_src emacs-lisp
    (setq-default js-indent-level 2
                  typescript-indent-level 2)
#+end_src

[[https://github.com/mooz/js2-mode/][js2-mode]] improves a lot on the builtin =js-mode=

#+begin_src emacs-lisp
  (use-package js2-mode
    :after eglot
    :mode
    ("\\.mjs\\'" . js2-mode)
    ("\\.jsx?\\'" . js2-jsx-mode)
    :hook
    (js2-mode . eglot-ensure)
    (js2-jsx-mode . eglot-ensure))
#+end_src

Prettier has my preference for formatting JavaScript and TypeScript

#+begin_src emacs-lisp
    (use-package prettier
      :config
      (setq prettier-enabled-parsers '
            (css html json markdown scss svelte toml typescript vue)))
#+end_src

TypeScript stuff

#+begin_src emacs-lisp
  (use-package typescript-mode
    :after eglot
    :mode
    ("\\.tsx?\\'" . typescript-mode)
    :hook (typescript-mode . eglot-ensure))
#+end_src

Prefer local packages from =node_modules= to global ones

#+begin_src emacs-lisp
  (use-package add-node-modules-path)
#+end_src

*** Web mode

[[https://web-mode.org/][web-mode]] handles HTML/CSS and JavaScript

#+begin_src emacs-lisp
  (use-package web-mode
    :mode "\\.svelte\\'"
    :after eglot
    :config
    (setq web-mode-markup-indent-offset 2
          web-mode-css-indent-offset 2
          web-mode-code-indent-offset 2
          web-mode-enable-auto-pairing t
          web-mode-enable-css-colorization t
          web-mode-enable-current-element-highlight t
          web-mode-enable-current-column-highlight t)

    (add-to-list 'web-mode-engines-alist '(("elixir" . "\\.html.heex\\'")
                                           ("jinja2" . "\\.jinja2\\'")
                                           ("python" . "\\.pt\\'")
                                           ("svelte" . "\\.svelte\\'")))
    :hook
    ((html-mode css-mode web-mode) . eglot-ensure))
#+end_src

*** Markdown

[[https://jblevins.org/projects/markdown-mode/][markdown-mode]] adds support for Markdown editing. =gfm-mode= supports GitHub Flavoured Markdown.

#+begin_src emacs-lisp
  (use-package markdown-mode
    :after eglot
    :mode
    (("README\\.md\\'" . gfm-mode)
     ("\\.md\\'" . markdown-mode)
     ("\\.markdown\\'" . markdown-mode))
    :init
    (setq markdown-command "multimarkdown")
    :hook
    (markdown-mode . display-fill-column-indicator-mode)
    (markdown-mode . eglot-ensure))
#+end_src

[[https://github.com/skeeto/impatient-mode][impatient-mode]] live renders HTML, but it can be made to work with Markdown with a custom filter

#+begin_src emacs-lisp
  (use-package impatient-mode
    :after markdown-mode
    :config
    (imp-set-user-filter 'markdown-filter))

    (defun markdown-filter (buffer)
      (princ
       (with-temp-buffer
         (let ((tmpname (buffer-name)))
           (set-buffer buffer)
           (set-buffer (markdown tmpname)) ; the function markdown is in `markdown-mode.el'
           (buffer-string)))
       (current-buffer)))
    #+end_src

*** Elixir

Add support for Elixir with [[https://github.com/elixir-editors/emacs-elixir][elixir-mode]]. The =elixir-format= hook sets up the correct formatter configuration when in a =projectile= project.

#+begin_src emacs-lisp :tangle no
  (use-package elixir-mode
    :after eglot
    :hook ((elixir-mode . eglot-ensure))
    :config
    (add-to-list 'auto-mode-alist '("\\.[hl]eex\\'" . elixir-mode)))
#+end_src

#+begin_src emacs-lisp
  (use-package elixir-ts-mode
    :after eglot
    :hook ((elixir-ts-mode . eglot-ensure)))
#+end_src

Add a [[https://github.com/ayrat555/mix.el][mix]] minor mode to call =mix= tasks from emacs.

#+begin_src emacs-lisp
  (use-package mix
    :hook
    (elixir-mode . mix-minor-mode))
#+end_src

*** Erlang

#+begin_src emacs-lisp
  (use-package erlang
    :mode
    ("\\.P\\'" . erlang-mode)
    ("\\.E\\'" . erlang-mode)
    ("\\.S\\'" . erlang-mode)
    :config
    (require 'erlang-start))
#+end_src

*** Rust

Rust support with [[https://github.com/rust-lang/rust-mode][rust-mode]].

#+begin_src emacs-lisp
  (use-package rust-mode
    :after eglot
    :hook
    (rust-mode . eglot-ensure)
    (rust-ts-mode . eglot-ensure)
    (before-save . eglot-format-buffer)
    ;; :init
    ;; (setq lsp-rust-analyzer-cargo-watch-command "clippy"
    ;;       lsp-rust-analyzer-server-display-inlay-hints t
    ;;       lsp-rust-analyzer-binding-mode-hints t
    ;;       lsp-rust-analyzer-display-lifetime-elision-hints-enable "always")
    )
#+end_src

Configure [[https://rust-analyzer.github.io][rust-analyzer]]

#+begin_src emacs-lisp
  (defun eb/ra-eglot-config (server)
    "initializationOptions for rust-analyzer"
    `(:diagnostics (:enable t)
      :imports (:granularity (:enforce :json-false :group "crate")
                :group t :merge
                (:glob t)
                :preferPrelude t
                :prefix "plain")
      :checkOnSave (:enable t
                    :command "clippy"
                    :allTargets t)
      :inlayHints (:bindingModeHints t
                   :chainingHints t
                   :lifetimeElisionHints (:enable "always" :useParameterNames t)
                   :maxLength nil
                   :typeHints (:enable t
                               :hideClosureInitialization :json-false
                               :hideNamedConstructor :json-false))
      :procMacro (:enable t)))

  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '(rust-mode . ("rust-analyzer" :initializationOptions eb/ra-eglot-config))))
#+end_src

Add [[https://github.com/kwrooijen/cargo.el][cargo]] support

#+begin_src emacs-lisp
  (use-package cargo
    :hook
    (rust-mode . cargo-minor-mode))
#+end_src

Flycheck support for Rust with [[https://github.com/flycheck/flycheck-rust][flycheck-rust]]

#+begin_src emacs-lisp
  (use-package flycheck-rust
    :hook
    (flycheck-mode . flycheck-rust-setup))
#+end_src

*** TOML

Support for TOML files with [[https://github.com/dryman/toml-mode.el][toml-mode]]

#+begin_src emacs-lisp :tangle no
  (use-package toml-mode
    :mode ("\\.toml\\'" . conf-toml-mode))
#+end_src

*** Docker

Add docker support with [[https://github.com/spotify/dockerfile-mode][dockerfile-mode]]

#+begin_src emacs-lisp
  (use-package dockerfile-mode
    :after eglot
    :hook (dockerfile-mode . eglot-ensure))
#+end_src

*** Bitbake / Yocto

#+begin_src emacs-lisp :tangle no
    (use-package bitbake-modes
      :straight (:type git :repo "https://bitbucket.org/olanilsson/bitbake-modes.git"))
#+end_src

*** INI

=ini-mode= provides highlighting for ini files

#+begin_src emacs-lisp
  (use-package ini-mode)
#+end_src

*** JSON

[[https://github.com/joshwnj/json-mode][json-mode]] extends the builtin =js-mode= with better syntax highlighting for JSON and adds some editing keybindings

#+begin_src emacs-lisp
  (use-package json-mode
    :after eglot
    :hook (json-mode . eglot-ensure))
#+end_src

*** CMake

Add [[https://melpa.org/#/cmake-mode][cmake-mode]]

#+begin_src emacs-lisp
  (use-package cmake-mode
    :after eglot
    :hook
    ((cmake-mode cmake-ts-mode) . eglot-ensure))
#+end_src

*** YAML

Use [[https://github.com/yoshiki/yaml-mode][yaml-mode]] to handle YAML files

#+begin_src emacs-lisp
  (use-package yaml-mode
    :after eglot
    :hook (yaml-mode . eglot-ensure))
#+end_src
*** C/C++

Enable clangd LSP for C and C++

#+begin_src emacs-lisp
  (use-package cc-mode
    :ensure nil
    :after eglot
    :hook
    ((c-mode c-ts-mode) . eglot-ensure)
    ((c++-mode c++-ts-mode) . eglot-ensure))
#+end_src

Add some flags to clangd

#+begin_src emacs-lisp
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '((c-mode c-ts-mode c++-mode c++-ts-mode)
                   . ("clangd"
                      "--malloc-trim"
                      "--log=error"
                      "--clang-tidy"
                      "--completion-style=detailed"))))
#+end_src

Add QML mode

#+begin_src emacs-lisp
      (use-package qml-mode
        :mode "\\.qml\\'")
#+end_src

Enable and configure =auto-insert-mode= for Horus projects

#+begin_src emacs-lisp
  (defconst my/generate-cpp-file-executable
    "~/workspace/horus/development/code-generation/generate-cpp-file.py"
    "Python program to generate a C++ boilerplate")

  (when (file-executable-p my/generate-cpp-file-executable)
    (define-auto-insert
      "\\.[ch]pp\\'"
      (lambda nil (call-process my/generate-cpp-file-executable nil t nil buffer-file-name))))


  (auto-insert-mode)
#+end_src

*** Meson

[[https://mesonbuild.com][meson]] is a build system designed to be as fast and as user-friendly as possible.

#+begin_src emacs-lisp
  (use-package meson-mode)
#+end_src
*** nix

Add [[https://github.com/NixOS/nix-mode][nix-mode]]

#+begin_src emacs-lisp
  (use-package nix-mode
    :after eglot
    :mode "\\.nix\\'"
    :hook (nix-mode . eglot-ensure))
#+end_src

Tell =nil= to use =nixfmt= for formatting nix files.

#+begin_src emacs-lisp
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 `(nix-mode . ("nil" :initializationOptions
                               (:formatting (:command ["nixfmt"]))))))
#+end_src

*** Common Lisp

Common Lisp does not use =lsp-mode=, but has it's own environment: [[https://github.com/slime/slime][SLIME]] or Superior Lisp Interaction Mode for Emacs.

#+begin_src emacs-lisp :tangle no
  (use-package slime
    :init
    (setq slime-lisp-implementations
          '((sbcl ("sbcl" "--load ~/.quicklisp/setup.lisp") :coding-system utf-8-unix))
          slime-default-lisp 'sbcl)
    :config
    (slime-setup '(slime-fancy slime-quicklisp slime-asdf))
    :mode
    (("\\.cl\\'" . lisp-mode))
    :hook
    (lisp-mode . (lambda () (slime-mode t)))
    (inferior-lisp-mode . (lambda () (inferior-slime-mode t))))
#+end_src

[[https://github.com/joaotavora/sly][SLY]] is a fork of SLIME, by the same author as =eglot=, with improved UX

#+begin_src emacs-lisp
  (use-package sly
    :mode
    (("\\.cl\\'" . lisp-mode))
    :config
    (setq sly-lisp-implementations
          '((sbcl ("sbcl") :coding-system utf-8-unix))
          sly-default-lisp 'sbcl))
#+end_src

*** Clojure

Similar to =lisp=, there is [[https://github.com/clojure-emacs/cider][CIDER]] (Clojure Interactive Development Environment that Rocks) for Clojure(Script)

#+begin_src emacs-lisp
  (use-package cider)
#+end_src
*** Terraform

[[https://github.com/emacsorphanage/terraform-mode][terraform-mode]] is a major mode for Terraform files

#+begin_src emacs-lisp
  (use-package terraform-mode
    :after eglot
    :hook (terraform-mode . eglot-ensure))
#+end_src

Register =terraform-ls= with eglot

#+begin_src emacs-lisp
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '(terraform-mode . ("terraform-ls" "serve"))))
#+end_src

*** Fish

[[https://github.com/wwwjfy/emacs-fish][fish-mode]] provides a major mode for fish shell scripts

#+begin_src emacs-lisp
  (use-package fish-mode
    :init
    (setq fish-enable-auto-indent t))
#+end_src

*** Zig

[[https://github.com/ziglang/zig-mode][zig-mode]] provides a major mode for the [[https://ziglang.org][zig programming language]]

#+begin_src emacs-lisp
  (use-package zig-mode
    :after eglot
    :hook (zig-mode . eglot-ensure))
#+end_src

*** Yuck

#+begin_src emacs-lisp
  (use-package yuck-mode)
#+end_src

*** Racket

#+begin_src emacs-lisp
  (use-package racket-mode
    :after eglot
    :hook (racket-mode . eglot-ensure))
#+end_src

*** Ruby

Let =ruby-mode= handle Ruby files

#+begin_src emacs-lisp
  (use-package ruby-mode
    :ensure nil
    :after eglot
    :hook (ruby-mode . eglot-ensure))
#+end_src

*** MapServer

Add highlighting for MapServer .map files

#+begin_src emacs-lisp :tangle no
  (use-package mapserver-mode
    :straight (:type git :host github :repo "AxxL/mapserver-emacs-mode")
    :mode ("\\.map\\'" . mapserver-mode))
#+end_src

*** Go

It's better than nothing.

#+begin_src emacs-lisp
  (use-package go-mode
    :after eglot
    :hook (go-mode . eglot-ensure))
#+end_src

*** Cucumber

[[https://github.com/michaelklishin/cucumber.el][feature-mode]] provides support for user stories written in Cucumber/Gherkin format.

#+begin_src emacs-lisp
  (use-package feature-mode
    :config
    (setq feature-use-docker-compose nil)
    :mode "\\.feature\\'")
#+end_src

*** Protobuf

[[https://github.com/protocolbuffers/protobuf/blob/main/editors/protobuf-mode.el][protobuf-mode]]

#+begin_src emacs-lisp
  (use-package protobuf-mode)
#+end_src

*** Just

[[https://github.com/leon-barrett/just-mode.el][just-mode]] provides syntax highlighting for Justfiles, for the command runner [[https://github.com/casey/just][just]]

#+begin_src emacs-lisp
  (use-package just-mode)
#+end_src

*** Python

Python

#+begin_src emacs-lisp :tangle no
  (use-package python-mode
    :hook
    ((python-mode python-ts-mode) . eglot-ensure))
#+end_src

#+begin_src emacs-lisp
  (use-package python
    :ensure nil
    :hook (python-base-mode . eglot-ensure)
    :init (setq python-indent-guess-indent-offset nil))
#+end_src



jinja2

#+begin_src emacs-lisp
  (use-package jinja2-mode)
#+end_src

*** Haskell

[[https://github.com/haskell/haskell-mode][haskell-mode]] for Haskell files

#+begin_src emacs-lisp
  (use-package haskell-mode
    :hook
    (((haskell-mode haskell-ts-mode) . eglot-ensure)
     turn-on-haskell-unicode-input-method))
#+end_src

*** Dhall

Dhall is a programmable configuration language that you can think of as: JSON + functions + types + imports

#+begin_src emacs-lisp
  (use-package dhall-mode
    :mode "\\.dhall\\'")
#+end_src

*** nushell

[[http://www.nushell.sh/][nushell]] is a new type of shell that operates on typed data

#+begin_src emacs-lisp
    (use-package nushell-ts-mode)
#+end_src

Register =nushell= LSP with eglot

#+begin_src emacs-lisp
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '(nushell-ts-mode . ("nu" "--lsp"))))
#+end_src

*** Lua

[[https://github.com/immerr/lua-mode][lua-mode]] for Lua support.

#+begin_src emacs-lisp
  (use-package lua-mode)
#+end_src

*** Svelte


#+begin_src emacs-lisp
  (use-package svelte-mode)
#+end_src

Use =svelteserver= as LSP

#+begin_src emacs-lisp
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '(svelte-mode . ("svelteserver" "--stdio"))))
#+end_src

*** Device Tree

Highlighting for device tree configuration.

#+begin_src emacs-lisp
    (use-package dts-mode)
#+end_src

*** OCaml

[[https://github.com/ocaml/tuareg][Tuareg]] is the recommended OCaml mode

#+begin_src emacs-lisp
        (use-package tuareg)
#+end_src

[[https://github.com/ocaml/merlin][Merlin]] adds context-sensitive completion

#+begin_src emacs-lisp
    (use-package merlin
      :init (setq merlin-command "ocamlmerlin")
      :hook (tuareg-mode . merlin-mode))
#+end_src


* Org

** Main org setup

[[https://orgmode.org][org-mode]] configuration

#+begin_src emacs-lisp
  (use-package org
    :general
    <<org-binds>>
    :custom
    <<org-customisations>>
    :hook
    <<org-hooks>>
    :config
    <<org-config>>)
#+end_src

Some keybindings for often used functions

#+name: org-binds
#+begin_src emacs-lisp :tangle no
  (:prefix my/leader
    "r" 'org-capture
    "a" 'org-agenda
    "s" 'org-store-link
    "L" 'org-store-link-global
    "O" 'org-open-at-point-global)
#+end_src

Customisations

#+name: org-customisations
#+begin_src emacs-lisp :tangle no
  (org-directory "~/org")
  (org-log-done t)
  (org-indent-indentation-per-level 2)
  (org-startup-indented t)
  (org-log-into-drawer t)
  (org-default-notes-file (expand-file-name "notes.org" org-directory))
  (org-return-follows-link t)
  (org-pretty-entities t)
  (org-hide-emphasis-markers t)
  (org-startup-with-inline-images t)
  (org-startup-with-latex-previews t)
  (org-image-actual-width '(300))
  (add-to-list 'org-export-backends 'md)
#+end_src

Hooks

#+name: org-hooks
#+begin_src emacs-lisp :tangle no
  ((org-mode . org-indent-mode)
   (org-mode . turn-on-visual-line-mode)
   (org-mode . variable-pitch-mode)
   (org-mode . (lambda () (display-line-numbers-mode -1)))
   (org-mode-indent . (lambda () (diminish 'org-indent-mode)))
   (org-agenda-mode . (lambda () (hl-line-mode 1))))
#+end_src

Configuration

#+name: org-config
#+begin_src emacs-lisp :tangle no
  (add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode))
  (dolist (face '((org-level-1 . 1.2)
                  (org-level-2 . 1.1)
                  (org-level-3 . 1.05)
                  (org-level-4 . 1.0)
                  (org-level-5 . 1.0)
                  (org-level-6 . 1.0)
                  (org-level-7 . 1.0)
                  (org-level-8 . 1.0)))
    (set-face-attribute (car face) nil :family my/variable-width-font :weight 'medium :height (cdr face)))
  (set-face-attribute 'org-document-title nil :family my/variable-width-font :weight 'bold :height 1.3)
  (set-face-attribute 'org-block nil :foreground 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-checkbox nil :inherit 'fixed-pitch)
#+end_src

=org-capture= allows creating and saving quick notes, links and TODOs

#+begin_src emacs-lisp
  (use-package org-capture
    ;; :straight org
    :ensure org
    :after org
    :config
    (setq org-capture-templates
          '(("t" "Todo" entry (file+headline "gtd.org")
             "* TODO %?\n  %i\n  %a")
            ("n" "Note" entry (file+olp+datetree "notes.org")
             "* %?\nEntered on %U\n  %i\n  %a")
            ("N" "Note (work)" entry (file+olp+datetree "work/notes.org")
             "* %?\nEntered on %U\n  %i\n  %a")
            ("c" "org-protocol-capture" entry (file+headline "inbox.org" "Links")
             "* %^{Title}\n\n[[%:link][%:description]]\n\n %i"
             :immediate-finish t))))
#+end_src

=org-datetree= allows you to organise captures by date. It is basically a set of headings representing the date, with the first level for the year, the second level for the month and the third for the day.

#+begin_src emacs-lisp
  (use-package org-datetree
    ;; :straight org
    :ensure org
    :after org)
#+end_src

=org-protocol= can be used to send data to emacs using =org-protocol://= URLs.

#+begin_src emacs-lisp
  (use-package org-protocol
    ;; :straight org
    :ensure org
    :after org
    :config
    (setq org-protocol-default-template-key "c"))
#+end_src

** org-roam

[[https://www.orgroam.com][org-roam]] helps with non-hierarchical note-taking. It uses the [[https://en.wikipedia.org/wiki/Zettelkasten][zettelkasten method]] to capture and organise notes and ideas.

#+begin_src emacs-lisp
  (use-package org-roam
    ;; :after org
    :custom
    (org-roam-directory "~/org-roam")
    (org-roam-completion-everywhere t)
    <<org-roam-templates>>
    :config
    (require 'org-roam-dailies)
    (org-roam-db-autosync-mode)
    :general
    (:prefix my/leader
      :keymaps 'global
      "n f" 'org-roam-node-find
      "n r" 'org-roam-node-random
      "n i" 'org-roam-node-insert
      "n l" 'org-roam-buffer-toggle
      :keymaps 'org-mode-map
      "n o" 'org-id-get-create
      "n t" 'org-roam-tag-add
      "n a" 'org-roam-alias-add
      "n l" 'org-roam-buffer-toggle)
    ("C-M-i" 'completion-at-point
     :keymaps 'org-roam-dailies-map
     "Y" 'org-roam-dailies-capture-yesterday
     "T" 'org-roam-dailies-capture-tomorrow)

    (:prefix my/leader "n d" '(:keymap org-roam-dailies-map)))
#+end_src

Org roam capture templates

#+name: org-roam-templates
#+begin_src emacs-lisp :tangle no
  (org-roam-capture-templates '(("d" "default" plain
                                 "%?"
                                 :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+date: %U\n")
                                 :unnarrowed t)
                                ("w" "work note" plain
                                 "%?"
                                 :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :work:\n")
                                 :unnarrowed t)
                                ("p" "project" plain
                                 "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n"
                                 :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :project:\n")
                                 :unnarrowed t)))
#+end_src

[[https://github.com/org-roam/org-roam-ui][org-roam-ui]] provides a frontend to explore and interact with =org-roam= notes. It can be started with =M-x org-roam-ui-mode RET=, and is then available on [[http://127.0.0.1:35901/]]. Updates are sent real-time through a WebSocket.
The settings provided here are also the defaults, set any to =nil= to disable.

#+begin_src emacs-lisp
  (use-package org-roam-ui
    :after org-roam
    :config
    (setq org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start t))
#+end_src

** org-chef

[[https://github.com/Chobbes/org-chef][org-chef]] is used to manage recipes and import them from supported websites

#+begin_src emacs-lisp
  (use-package org-chef
    :after org-capture
    :config
    (add-to-list 'org-capture-templates '("r" "Recipes"))
    (add-to-list 'org-capture-templates '("rr" "Recipe" entry (file "~/org/cookbook.org")
                                          "%(org-chef-get-recipe-from-url)"
                                          :empty-lines 1))
    (add-to-list 'org-capture-templates '("rm" "Manual recipe" entry (file "~/org/cookbook.org")
                                          "* %^{Recipe title: }\n  :PROPERTIES:\n  :source-url:\n  :servings:\n  :prep-time:\n  :cook-time:\n  :ready-in:\n  :END:\n** Ingredients\n  %?\n** Directions\n\n")))
#+end_src

* Misc

** direnv

[[https://github.com/purcell/envrc][envrc]] adds support for [[https://direnv.net][direnv]] to emacs. =envrc= works on a per-buffer basis, so when you open multiple projects the environments don't pollute eachother. NOTE: this should be loaded last.

#+name: envrc-init
#+begin_src emacs-lisp :tangle no
  (use-package envrc
    :init
    (envrc-global-mode)
    :general
    (:prefix my/leader "e" '(:keymap envrc-command-map)))
#+end_src

** Writing

Enable flyspell

#+begin_src emacs-lisp
  (use-package flyspell
    :ensure nil
    :init
    (setq ispell-list-command "--list")
    :hook
    (text-mode . flyspell-mode))

  (use-package flyspell-correct
    :after flyspell
    :general

    (:keymaps 'flyspell-mode-map
              "C-;" 'flyspell-correct-wrapper))
#+end_src

Use ivy for flyspell suggestions. Use =M-o= to switch to the actions (e.g. saving a word to your dictionary).

#+begin_src emacs-lisp :tangle no
  (use-package flyspell-correct-ivy
    :after flyspell-correct)
#+end_src

** Load local init file

If =local-init.el= exists, load it.

#+begin_src emacs-lisp
  (when (file-readable-p my/local-init-file)
    (load my/local-init-file))
#+end_src

#+begin_comment
DO NOT ADD ANYTHING BELOW THIS
#+end_comment

#+begin_src emacs-lisp :exports none
  <<envrc-init>>
#+end_src