dot-emacs/init.org

60 KiB

;;; -- lexical-binding: t -- #+TITLE:Emacs Configuration

Preamble

Inspiration

I started my own emacs config after becoming fed up with how slow Spacemacs, which is my go-to config of emacs, was for me, and how rigid the experience was to me. I never felt like I could really change Spacemacs to fit my own needs beyond what the config offered itself.

After looking online for guides on how to do a "literate configuration" and stumbled upon Sophie Bosio's emacs blogs and git repo.

Cool Packages to remember

These are some packages which I might want to use in the future, or maybe not, but I definitely want to remember that they exist.

look at org-bullets to make a package which turns -- and --- into $\textemdash$ and $\textemdash$. -> https://github.com/jorgenschaefer/typoel perchance?

Start

Lexical Scoping

  ;;; -*- lexical-binding: t -*-

Tangling init.org

From Sophie's emacs.d:

(defun tangle-init ()
  "If the current buffer is init.org the code-blocks are
tangled, and the tangled file is compiled."
  (when (equal (buffer-file-name)
               (expand-file-name (concat user-emacs-directory "init.org")))
    ;; Avoid running hooks when tangling.
    (let ((prog-mode-hook nil))
      (org-babel-tangle)
      ;; (byte-compile-file (concat user-emacs-directory "init.el"))
      )))

(add-hook 'after-save-hook 'tangle-init)

Startup performance

Usually, I like to run emacs as a daemon and only ever open new emacs clients, but that often doesn't work properly and definitely doesn't work when actively working on an emacs config, so this snippet might be useful.

  (setq gc-cons-percentage      0.6)
  (setq read-process-output-max (* 1024 1024)) ;; 1mb

Use GC magic hack.

  (use-package gcmh
    :config
    (setq gcmh-idle-delay 5
          gcmh-high-cons-threshold (* 100 1024 1024))  ; 100mb
    (gcmh-mode 1))

I have no idea what this does, but apparently it's an optimisation.

  (setq idle-update-delay 1.0)

Cleaning up elements

Remove visual elements:

  (dolist (mode
           '(tool-bar-mode
             scroll-bar-mode
             menu-bar-mode
             blink-cursor-mode))
    (funcall mode 0))
  (setq ring-bell-function 'ignore
        initial-scratch-message nil
        inhibit-startup-message t
        use-dialog-box nil)

Warnings crop up every so often and they are usually unimportant. I disable this buffer when actively working on my config.

(setq native-comp-async-report-warnings-errors 'silent) ;; native-comp warning
(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
(setq warning-minimum-level :emergency)
(setq inhibit-message t)

Backups / Auto-saves

  (defvar emacs-autosave-directory
    (concat user-emacs-directory "autosaves/")
    "This variable dictates where to put auto saves. It is set to a
    directory called autosaves located wherever your .emacs.d/ is
    located.")

  ;; Sets all files to be backed up and auto saved in a single directory.
  (setq backup-directory-alist
        `((".*" . ,emacs-autosave-directory))
        auto-save-file-name-transforms
        `((".*" ,emacs-autosave-directory t)))

Auto-save files:

  (use-package auto-save-buffers-enhanced
    :straight t
    :config
    (auto-save-buffers-enhanced t)
    (setq auto-save-buffers-enhanced-exclude-regexps '("init.org")))

Package repositories

Use straight, and it's bootstrapping code:

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Disable package.el in the early init file:

(setq package-enable-at-startup nil)

Then, install use-package and tell it to always assume :straight:

(straight-use-package 'use-package)
(setq straight-use-package-by-default t)

load org early to work around version mismatches:

(use-package org
  :straight (:type built-in))

Add package repositories and rank them by priority

  (setq package-archives
  	  '(("GNU ELPA"     . "https://elpa.gnu.org/packages/")
  		("MELPA"        . "https://melpa.org/packages/")
  		("ORG"          . "https://orgmode.org/elpa/")
  		("MELPA Stable" . "https://stable.melpa.org/packages/")
  		("nongnu"       . "https://elpa.nongnu.org/nongnu/"))
  	  package-archive-priorities
  	  '(("GNU ELPA"     . 20)
  		("MELPA"        . 15)
  		("ORG"          . 10)
  		("MELPA Stable" . 5)
  		("nongnu"       . 0)))

  (package-initialize)
  ; (package-refresh-contents)
  (defvar local-lisp (concat user-emacs-directory "local-lisp/"))
  (add-to-list 'load-path  local-lisp)
  (let ((default-directory local-lisp))
    (normal-top-level-add-subdirs-to-load-path))

Global Variables

(set-language-environment "UTF-8")
(delete-selection-mode   t) ;; Replace selected text when yanking
(dirtrack-mode           t) ;; Directory tracking in shell
(global-so-long-mode     t) ;; Mitigate performance for long lines
(global-visual-line-mode t) ;; Break lines instead of truncating them
(global-auto-revert-mode t) ;; Revert buffers automatically when they change
(recentf-mode            t) ;; Remember recently opened files
(savehist-mode           t) ;; Remember minibuffer prompt history
(save-place-mode         t) ;; Remember last cursor location in file
(show-paren-mode         t) ;; Highlight matching parentheses
(setq-default tab-width 4
              fill-column 80
              indent-tabs-mode nil)
(setq use-short-answers t)
(setq initial-major-mode 'org-mode)
(use-package exec-path-from-shell
  :straight t
  :config
  (when (daemonp)
    (exec-path-from-shell-initialize)))
(use-package keychain-environment
  :straight t
  :init
  (keychain-refresh-environment))
(use-package keychain-environment
  :straight t
  :defer t
  :config
  (keychain-refresh-environment))

Keymap:

  (defvar custom-bindings-map (make-keymap)
    "A keymap for custom keybindings.")

  (define-minor-mode custom-bindings-mode
    "A mode that activates custom keybindings."
    :init-value t
    :keymap custom-bindings-map)

Visuals

Borders, Frames & Windows

(add-to-list 'default-frame-alist '(internal-border-width . 8 ))
(set-fringe-mode 4)
(setq-default fringes-outside-margins 0)
(setq-default indicate-buffer-boundaries nil)
(setq-default indicate-empty-lines nil)
(set-face-attribute 'header-line t :inherit 'default)

Themes

Light theme for writing

  (use-package gruvbox-theme
    :straight t)

Doom Themes as default theme

  ;; doom needs this somehow
  (use-package all-the-icons
    :straight t)
  (use-package doom-themes
    :straight t
    :config
    (setq doom-themes-enable-bold t
          doom-themes-enable-italic t)
    (doom-themes-org-config))

Manage themes, use dark-theme by default:

(defvar nemo/dark-theme 'gruvbox-dark-medium)
(defvar nemo/light-theme 'gruvbox-light-medium)

(add-hook 'after-init-hook (lambda () (load-theme nemo/dark-theme t)))

Disable themes before loading a new one, from here.

  (defadvice load-theme
      (before disable-before-load (theme &optional no-confirm no-enable) activate)
    (mapc 'disable-theme custom-enabled-themes))

Helper function for switching themes.

(defun nemo/set-light-theme ()
  "load the light theme."
  (interactive)
  (load-theme nemo/light-theme t))

(defun nemo/set-dark-theme ()
  "load the dark theme."
  (interactive)
  (load-theme nemo/dark-theme t))

emacs-related

(setq help-window-select t
      help-window-keep-selected t)

Fonts

Font size (height) in emacs works in $\frac{1}{10}$ths of points, so 110 is the same as 11 points.

  (defvar nemo/font-height-mono 110)
  (defvar nemo/font-height-sans 120)
(defun apply-if-gui (&rest action)
  "apply ACTION if emacs is run in GUI context, regardless of daemonp or display-graphic-p"
  (if (daemonp)
      (add-hook 'after-make-frame-functions
                (lambda (frame)
                  (select-frame frame)
                  (if (display-graphic-p frame)
                      (apply action))))
    (if (display-graphic-p)
        (apply action))))


(set-face-attribute 'default nil :font "monospace" :height nemo/font-height-mono)
(set-face-attribute 'fixed-pitch nil :font "monospace" :height nemo/font-height-mono)
(set-face-attribute 'variable-pitch nil :font "sans-serif" :height nemo/font-height-sans)

;; (when (member "SF Mono" (font-family-list))
;;   (set-face-attribute 'default nil :font "SF Mono" :height nemo/font-height-mono)
;;   (set-face-attribute 'fixed-pitch nil :family "SF Mono" :height nemo/font-height-mono))
(when (member "SF Pro Text" (font-family-list))
  (set-face-attribute 'variable-pitch nil :family "SF Pro Text" :height nemo/font-height-sans))

(defvar nemo/nerd-fonts-font "Symbols Nerd Font")

(defun nemo/set-fallback-fonts ()
  "Set fallback fonts for icons/symbols."
  (when (member nemo/nerd-fonts-font (font-family-list))
    (set-fontset-font t nil (font-spec :height nemo/font-height-mono :font "Symbols Nerd Font")))
  )

(apply-if-gui #'nemo/set-fallback-fonts)

Use nerd-icons and apple emojis

  (use-package nerd-icons
	:straight t)

  (use-package emojify
	:straight t
    :config
    (when (member "Apple Color Emoji" (font-family-list))
      (set-fontset-font t 'symbol (font-spec :family "Apple Color Emoji") nil 'prepend)))

Use both fixed and variable pitched fonts and faces.

  (use-package mixed-pitch
	:straight t
    :hook ((org-mode . mixed-pitch-mode)
           (LaTeX-mode . mixed-pitch-mode)))

Mode Line

Honestly not very happy with this at the moment, but it's kind of hacked together based on Sophie's and Amit's modelines.

;; Mode line setup
(column-number-mode t) ;; Show current column number in mode line
(setq-default
 mode-line-format
 '((:propertize "λ" face mode-line-emphasis)
   "  "
   mode-line-buffer-identification
   "  "
   ; read-only or modified status
   (:eval
    (cond (buffer-read-only
           (propertize "  " 'face 'mode-line-read-only-face))
          ((buffer-modified-p)
           (propertize " 󰳻 " 'face 'mode-line-modified-face))
          (t "  " 'face 'mode-line)))
   ; directory and buffer/file name
   (:propertize (:eval (shorten-directory default-directory 30))
                face mode-line-folder-face)
   (:propertize "%b"
                face mode-line-filename-face)
   ;; Version control info
   (:eval (when-let (vc vc-mode)
  		  ;; Use a pretty branch symbol in front of the branch name
  		  (list (propertize " " 'face 'font-lock-keyword-face)
  				(propertize (substring vc 5)
  							'face 'font-lock-doc-face))))


   "   "
   (:propertize mode-name)
   (:propertize (length local-minor-modes))
   ; (global-mode-string global-mode-string)
   (:eval (propertize
  		 " " 'display
  		 `((space :align-to
  				  (-  (+ right right-fringe right-margin)
  					  ,(+ 1 (string-width "%p  %4l:%c")))))))
   ;; Line and column numbers
   mode-line-percent-position
   " "
   (:propertize "%4l:" face mode-line-position-face)
   (:eval (propertize "%c" 'face
                      (if (>= (current-column) 80)
                          'mode-line-80col-face
                        'mode-line-position-face)))
   ))

;; Helper function
(defun shorten-directory (dir max-length)
  "Show up to `max-length' characters of a directory name `dir'."
  (let ((path (reverse (split-string (abbreviate-file-name dir) "/")))
        (output ""))
    (when (and path (equal "" (car path)))
      (setq path (cdr path)))
    (while (and path (< (length output) (- max-length 4)))
      (setq output (concat (car path) "/" output))
      (setq path (cdr path)))
    (when path
      (setq output (concat ".../" output)))
    output))

;; Extra mode line faces
(make-face 'mode-line-read-only-face)
(make-face 'mode-line-modified-face)
(make-face 'mode-line-folder-face)
(make-face 'mode-line-filename-face)
(make-face 'mode-line-position-face)
(make-face 'mode-line-mode-face)
(make-face 'mode-line-80col-face)

(set-face-attribute 'mode-line-read-only-face nil
    :inherit 'mode-line-face
    :foreground "#4271ae"
    :box '(:line-width 2 :color "#4271ae"))
(set-face-attribute 'mode-line-modified-face nil
    :inherit 'mode-line-face
    :foreground "#c82829"
    :background "#ffffff"
    :box '(:line-width 2 :color "#c82829"))
(set-face-attribute 'mode-line-folder-face nil
    :inherit 'mode-line-face
    :foreground "gray60")
(set-face-attribute 'mode-line-filename-face nil
    :inherit 'mode-line-face
    :foreground "#eab700"
    :weight 'bold)
(set-face-attribute 'mode-line-position-face nil
    :inherit 'mode-line-face)
(set-face-attribute 'mode-line-mode-face nil
    :inherit 'mode-line-face
    :foreground "gray80")
(set-face-attribute 'mode-line-80col-face nil
    :inherit 'mode-line-position-face
    :foreground "black" :background "#eab700")

Packages

general.el

(defconst nemo/leader-evil "SPC")
(defconst nemo/leader-global "C-c")
(defconst nemo/major-key "m")

(defconst nemo/leader-major-evil (concat nemo/leader-evil " " nemo/major-key))
(defconst nemo/leader-major-global (concat nemo/leader-global " " nemo/major-key))

(use-package general
  :straight t
  :config
  (general-define-key
   :states '(emacs normal)
   :prefix-map 'nemo/leader-prefix-map
   :prefix-command 'nemo/leader-prefix-command
   :global-prefix nemo/leader-global
   :prefix nemo/leader-evil)
  (general-create-definer leader-def
    :prefix-command 'nemo/leader-prefix-command
    :prefix-map 'nemo/leader-prefix-map)
  (general-create-definer leader-other-def
    :states '(emacs normal)
   :prefix-map 'nemo/leader-prefix-map
   :prefix-command 'nemo/leader-prefix-command
    :global-prefix nemo/leader-global
    :prefix nemo/leader-evil
    )
  (general-create-definer leader-major-def
    :states '(emacs normal)
    :global-prefix nemo/leader-global
    :prefix nemo/leader-evil
    )
  ;; magit uses with-editor-mode to spawn the git process
  ;; in with the emacs client as the $EDITOR
  (general-def '(emacs normal) 'with-editor-mode-map ",," 'with-editor-finish)
  (leader-def
    "<TAB>" #'nemo/switch-to-last-buffer
    "d" #'duplicate-line
    "gb" #'xref-go-back
    "gf" #'xref-go-forward
    "bk" #'switch-to-prev-buffer
    "bp" #'switch-to-prev-buffer
    "bj" #'switch-to-next-buffer
    "bn" #'switch-to-next-buffer
    "bb" #'consult-buffer
    "bd" #'kill-current-buffer
    "bs" #'scratch-buffer
    "wd" #'delete-window
    "wo" #'delete-other-windows
    "ff" #'find-file
    "fi" #'nemo/edit-init-org
    "fs" #'save-buffer
    )
  )

Web Search

  (use-package engine-mode
	:straight t
    :defer t
    :config
    (defengine duckduckgo
               "https://duckduckgo.com/?q=%s"
               :keybinding "g")

    (defengine twitter
               "https://twitter.com/search?q=%s"
               :keybinding "x")

    (defengine wikipedia
               "https://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
               :keybinding "w"
               :docstring "Searchin' the wikis.")

    (defengine wiktionary
               "https://www.wikipedia.org/search-redirect.php?family=wiktionary&language=en&go=Go&search=%s"
               :keybinding "d")

    (defengine wolfram-alpha
               "https://www.wolframalpha.com/input/?i=%s")

    (defengine youtube
               "https://www.youtube.com/results?aq=f&oq=&search_query=%s"
               :keybinding "y")
    (engine-mode t)
    )

magit

try out diff-hl for highlighting diffs in magit:

(use-package diff-hl
	:straight t
  :config
  (global-diff-hl-mode))

magit is awesome and the number one reason why I use Emacs!

(use-package magit
  :straight t
  :defer t
  :general
  (leader-def
    "gSh" 'magit-status-here
    "gs" #'magit-status
    "gSb" 'magit-blame)
  :config
  (setq magit-mode-quit-window 'magit-restore-window-configuration
        ;;magit-auto-revert-mode t
        ))

magit-todos sounds cool.

  (use-package magit-todos
    :after magit
	:straight t
    :config
    (magit-todos-mode t))

And forge sounds cool as well.

  (use-package forge
	:straight t
    :after magit)

which-key

This is one of those features of Spacemacs that is super useful.

  (use-package which-key
	:straight t
    :config
    (which-key-mode))

rainbow-delimiters

raimbow delimiters colours matching delimiters with different colours. Used by Spacemacs as well.

  (use-package rainbow-delimiters
	:straight t
    :hook (prog-mode . rainbow-delimiters-mode))

evil

(use-package evil
  :straight t
  :init
  (setq evil-want-C-u-scroll t)
  (setq evil-want-keybinding nil)
  (setq evil-want-integration t)
  (setq evil-undo-system 'undo-fu)
  :config
  (evil-mode 1)

Evil key-binds, documentation for how this works is here:

(leader-def
 "wj" 'evil-window-down
 "wk" 'evil-window-up
 "wh" 'evil-window-left
 "wl" 'evil-window-right
 )

Advice evil-search-forward (bound to /) to push the current marker onto the xref stack to allow for returning to where I started searching from:

(defun nemo/evil-search-forward--push-xref
    (&optional _regex-p _no-recursive-edit)
  "push current marker to xref stack when forward searching."
  (xref-push-marker-stack))

(advice-add #'evil-search-forward
            :before #'nemo/evil-search-forward--push-xref
            '((name . "evil-search-push-xref")))

Close :config.

  )
  (use-package evil-surround
	:straight t
    :defer t
    :config
    (global-evil-surround-mode 1))
(use-package evil-collection
  :after evil
  :straight t
  :init (setq evil-collection-key-blacklist '("C-c <tab>" "<SPC>"))
  :config
  (evil-collection-init))

undo-fu

Also use undo-fu, which evil can use.

  (use-package undo-fu
	:straight t
    :defer t)

Projectile

(use-package projectile
  :straight t
  :defer t
  :general
  (leader-def
    "p" '(:keymap project-prefix-map :wk "Projectile")
    )
  :config
  (setq projectile-project-search-path '("~/code/"))
  (projectile-mode))

ripgrep & Co.

(use-package ripgrep
  :straight t
  :defer t)

(use-package rg
  :straight t
  :defer t)

  (use-package wgrep
	:straight t
    :defer t)

VTerm

  (use-package vterm
    :straight t)

Stuff

Mini-buffer escape

(setq minibuffer-prompt-properties
	  '(read-only t
        intangible t
        cursor-intangible t
        face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
  (defun nemo/abort-minibuffer-if-active ()
    "Abort the minibuffer if it is active."
    (interactive)
    (when (active-minibuffer-window)
      (abort-recursive-edit)))

  (global-set-key (kbd "<escape>") 'nemo/abort-minibuffer-if-active)

Writing

Olivetti

(use-package olivetti
  :straight t
  :defer t
  ;; :bind (:map custom-bindings-map ("C-c o" . olivetti-mode))
  :hook (org-mode . olivetti-mode)
  :hook (olivetti-mode-on . (lambda () (olivetti-set-width 88)))
  :config
  (setq olivetti-style t))

Spelling

(use-package jinx
  :straight t
  :hook (emacs-startup . global-jinx-mode)
  :bind (("M-$"   . jinx-correct)
         ("C-M-$" . jinx-languages))
  :config
  (leader-def "Ss" 'jinx-correct)
  (setq jinx-languages "en_GB dk_DK de_DE"))

LaTeX

(use-package auctex
  :straight t
  :hook
  (LaTeX-mode . turn-on-prettify-symbols-mode)
  (LaTeX-mode . reftex-mode)
  ;; (LaTeX-mode . (lambda () (corfu-mode -1)))
  ;; (LaTeX-mode . outline-minor-mode)
  (LaTeX-mode . olivetti-mode)
  :config
  (setq TeX-view-program-selection '((output-pdf "PDF Tools"))
        TeX-view-program-list '(("PDF Tools" TeX-pdf-tools-sync-view))
        TeX-source-correlate-start-server t))

PDF tools for latex previewing:

(use-package pdf-tools
  :straight t
  :defer t
  :mode ("\\.pdf\\'" . pdf-view-mode)
  :config
  (pdf-loader-install)
  (setq-default pdf-view-display-size 'fit-height)
  (setq pdf-view-continuous t)
  (setq +latex-viewers '(pdf-tools))
  (leader-major-def
    :keymaps 'pdf-view-mode-map
    "g b" #'pdf-history-backward
    "g f" #'pdf-history-forward)
  (general-def :keymaps 'pdf-view-mode-map
    "j" (lambda() (interactive) (pdf-view-scroll-up-or-next-page 20))
    "k" (lambda() (interactive) (pdf-view-scroll-down-or-previous-page 20)))
  )

Org

Taken from here.

(use-package org
  :straight (:type built-in)
  :defer t
  :hook (org-mode . variable-pitch-mode)
  ;; I basically always want to be running =visual-line-mode= anyway, but certainly in org-mode.
  :hook (org-mode . visual-line-mode)
  :hook (org-mode . nemo/prettify-symbols-setup)
  :general-config
  (general-define-key
   :prefix-map 'nemo/org-map
   "i" '(:ignore t :which-key "Insert..")
   "ih" '("Heading" . org-insert-heading)
   "is" '("Subheading" . org-insert-subheading)
   "ii" '("Item" . org-insert-item)
   "ib" '("Block..". org-insert-structure-template)
    "il" '("Link" . org-insert-link)
   )
  (leader-major-def
    :keymaps 'org-mode-map
    "m" '(:keymap nemo/org-map :wk "Org")
    )
  (general-def 'normal 'org-src-mode-map ",," 'org-edit-src-exit)
  (general-def 'normal
    :keymaps 'org-mode-map
    "TAB" 'org-cycle
    "RET" 'org-open-at-point)
  :config

Change heading font sizes:

(custom-set-faces
 '(org-document-title ((t (:height 1.8))))
 '(outline-1          ((t (:height 1.35))))
 '(outline-2          ((t (:height 1.3))))
 '(outline-3          ((t (:height 1.2))))
 '(outline-4          ((t (:height 1.1))))
 '(outline-5          ((t (:height 1.1))))
 '(outline-6          ((t (:height 1.1))))
 '(outline-8          ((t (:height 1.1))))
 '(outline-9          ((t (:height 1.1)))))

Open Org files with the content folded away:

(setq org-startup-folded 'content)

Enable LaTeX previews.

(setq org-startup-with-latex-preview t)

Inline images as well.

(setq org-startup-with-inline-images t)

In case LaTeX previews are too small, use this to increase them.

(plist-put org-format-latex-options :scale 1.35)

pretty-entities allows for latex symbols to be embedded into org-mode. org-hide-leading-stars hides all but one star on org headings.

(setq org-adapt-indentation t
      org-hide-leading-stars t
      org-pretty-entities-include-sub-superscripts t
      org-pretty-entities t)

Let Emacs act according to the language's rules inside of a source block.

(setq org-src-fontify-natively t
      org-src-tab-acts-natively t
      org-edit-src-content-indentation 0)

End :config

)

Install various export packages:

(use-package ox-epub
  :straight t)

Use org-bullets for fancy headline markers

(use-package org-bullets
  :straight t
  :hook (org-mode . (lambda () (org-bullets-mode 1)))
  :config)

Use typo-mode in org-mode for en and em dashes:

(require 'typo)

(typo-global-mode 1)
(add-hook 'org-mode-hook 'typo-mode)

Use org-fragtog to show embedded LaTeX fragments when in insert mode.

(use-package org-fragtog
  :straight t
  :hook (org-mode . org-fragtog-mode))

Using org-appear we can hide emphasis markers for italic, bold, etc. and show when editing the surrounded word.

(use-package org-appear
  :straight t
  :commands (org-appear-mode)
  :hook (org-mode . org-appear-mode)
  :config
  (setq org-hide-emphasis-markers t)
  (setq org-appear-autoemphasis t ;; show /../ *..* =..= ~..~ tokens
        org-appear-autolinks t ;; show link hyperlinks when editing
        org-appear-autosubmarkers t)) ;; show _.._ and superscript markers
(use-package org-modern
  :straight t
  :defer t
  :after org
  :hook (org-mode . org-modern-mode))

Change org face fonts and font sizes for headers.

(require 'org-faces)
;; size org levels differently
(dolist (face '((org-document-title . 1.8)
                (org-level-1 . 1.35)
                (org-level-2 . 1.3)
                (org-level-3 . 1.2)
                (org-level-4 . 1.1)
                (org-level-5 . 1.1)
                (org-level-6 . 1.1)
                (org-level-7 . 1.1)
                (org-level-8 . 1.1)))
  ;; set the font for each of them to sans-serif
  (set-face-attribute
   (car face)
   nil
   :font "SF Pro Text"
   :weight 'bold
   :height (cdr face)))

Fix org-indent to be fixed-pitch.

(require 'org-indent)
(set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch))

Make sure that faces like code blocks or verbatim text are still using a monospaced font:

(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch :height 0.85)
(set-face-attribute 'org-code nil :foreground nil :inherit '(shadow fixed-pitch) :height 0.85)
(set-face-attribute 'org-indent nil :foreground nil :inherit '(org-hide fixed-pitch) :height 0.85)
(set-face-attribute 'org-verbatim nil :foreground nil :inherit '(shadow fixed-pitch) :height 0.85)
(set-face-attribute 'org-special-keyword nil :foreground nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-meta-line nil :foreground nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-checkbox nil :foreground nil :inherit 'fixed-pitch)

prettify symbols for quotes and source blocks in org-mode.

(defun nemo/prettify-symbols-setup ()
  ;; org-babel
  (push '("#+BEGIN_SRC" . ?≫) prettify-symbols-alist)
  (push '("#+END_SRC" . ?≫) prettify-symbols-alist)
  (push '("#+begin_src" . ?≫) prettify-symbols-alist)
  (push '("#+end_src" . ?≫) prettify-symbols-alist)

  (push '("#+BEGIN_QUOTE" . ?❝) prettify-symbols-alist)
  (push '("#+END_QUOTE" . ?❞) prettify-symbols-alist)

  (prettify-symbols-mode))

svg-tags-mode for fancy SVG images

(use-package svg-tag-mode
  :straight t
  :config
  (setq svg-tag-tags
        '((":TODO:" . ((lambda (tag) (svg-tag-make "TODO"))))))
  )

org-agenda

(require 'org)

(setq org-agenda-start-on-weekday nil
      org-agenda-block-separator nil
      org-agenda-remove-tags t)

(use-package org-super-agenda
  :straight t
  :after org
  :config
  (org-super-agenda-mode))

(setq org-agenda-files (list "~/Shared/agenda.org"
                             "~/notes.org"
                             "~/projects.org"))

(add-hook 'emacs-startup-hook
		  (lambda () (progn (org-agenda nil "a")
					        (delete-other-windows)
					        (olivetti-mode))))

Navigation / mini-buffer

Realistically, I will probably never use 90% of Helm's functionality, so vertico should be sufficient.

Move Text

Use the move-text package to move the current line or selection up or down with M-j and M-k.

(use-package move-text
  :straight t
  :defer t
  :bind (("M-j" . move-text-down)
         ("M-k" . move-text-up)))

Treemacs

(use-package treemacs
  :straight t
  ;; hijack projectile prefix because they fit together
  :general
  (general-define-key
   :prefix-map 'project-prefix-map
   "t" '("Treemacs" . treemacs-select-window)
   )
  :config
  (setq treemacs-width 25)
  )


(use-package treemacs-evil
  :after (treemacs evil)
  :straight t)

(use-package treemacs-projectile
  :after (treemacs projectile)
  :straight t)

(use-package treemacs-icons-dired
  :hook (dired-mode . treemacs-icons-dired-enable-once)
  :straight t)

(use-package treemacs-magit
  :after (treemacs magit)
  :straight t)

Vertico

(use-package vertico
  :straight t
  :bind (:map minibuffer-local-map
              ("C-h" . backward-kill-sexp))
  :config
  (vertico-mode 1)
  (setq vertico-count 25
        completion-ignore-case t
        read-buffer-completion-ignore-case t
        read-file-name-completion-ignore-case t)
  (vertico-multiform-mode)
  )

Use vertico-posframe to make the vertico buffer floating in the centre of the frame.

(use-package vertico-posframe
  :straight t
  ;; Ensure posframe is always restored when exiting a minibuffer
  :hook (minibuffer-exit . (lambda () (vertico-posframe-mode 1)))
  :config
  (vertico-posframe-mode 1)
  (setq vertico-posframe-height vertico-count
        vertico-multiform-commands
        '((consult-line (:not posframe))
          (consult-ripgrep (:not posframe))
          (embark-act (:not posframe))
          (t posframe))
        ))
(use-package savehist 
  :straight t 
  :init (savehist-mode))

Use consult-xref for lsp-xref and xref-find-references.

(use-package consult
  :straight t
  :bind (:map custom-bindings-map
              ("C-x b" . consult-buffer)
              ("M-g g" . consult-goto-line))
  :init
  (setq xref-show-xrefs-function 'consult-xref
        xref-show-definitions-function 'consult-xref)
  :config
  ())
(defun my-consult-line-wrapper ()
  (interactive)
  (vertico-posframe-mode -1)
  (consult-line)
  (vertico-posframe-mode 1))

(defun my-consult-ripgrep-wrapper ()
  (interactive)
  (vertico-posframe-mode -1)
  (consult-ripgrep)
  (vertico-posframe-mode 1))

(bind-key "C-s"   'consult-line custom-bindings-map)
(bind-key "C-M-s" 'consult-ripgrep custom-bindings-map)

marginalia adds marginalia into the mini-buffer, for example key-binds in M-x

(use-package marginalia
  :straight t
  :init
  (marginalia-mode 1))
(use-package vertico-prescient
  :straight t
  :after vertico
  :init
  (vertico-prescient-mode 1)
  )

Company - Auto-Completion

(use-package company
  :straight t
  :defer t
  :hook (after-init . company-mode)
  :hook (prog-mode . company-mode)
  :config
  (setq
   company-minimum-prefix-length 1
   company-idle-delay 0.0))

Use ispell with company mode for completion in text-modes

(use-package ispell
  :straight t
  :defer t
  :config
  (setq ispell-dictionary "en_GB"))

COMMENT Corfu - Auto-Completion

Auto-completion using corfu:

(use-package corfu
  :straight t
  :custom
  ;; Enable auto completion
  (corfu-auto t)
  ;; Enable cycling for `corfu-next/previous'
  (corfu-cycle t)
  ;; No delay
  (corfu-auto-delay 0)
  ;; Start when this many characters have been typed
  (corfu-auto-prefix 1)
  ;; Short delay
  (corfu-popupinfo-delay 0.5)
  ;; Preselect the first suggestion
  (corfu-preselect 'first)
  (corfu-on-exact-match nil)
  :config
  (global-corfu-mode))

(use-package corfu-terminal
  :straight t
  :defer t
  :hook (before-make-frame .
                           (lambda ()
                             (corfu-terminal-mode
                              (if (display-graphic-p) -1 +1))))
  )

(use-package emacs
  ;; :custom
  ;; TODO
  :init
  ;; TAB cycle if there are only few candidates
  ;; (setq completion-cycle-threshold 3)

  (setq enable-recursive-minibuffers t)
  ;; Hide commands in M-x which do not apply to the current mode.  Corfu
  ;; commands are hidden, since they are not supposed to be used via M-x.
  (setq read-extended-command-predicate
        #'command-completion-default-include-p)

  (setq text-mode-ispell-word-completion nil)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (setq tab-always-indent 'complete))

Use kind-icon to decorate corfu completion candidates:

(use-package kind-icon
  :straight t
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

This package seems to slow down search quite a bit in common buffers like find-file and exectue-extended-comand:

(use-package cape
  :straight t
  ;; 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-symbol)
         ("C-c p a"  . cape-abbrev)
         ("C-c p l"  . cape-line)
         ("C-c p w"  . cape-dict)
         ("C-c p \\" . cape-tex)
         ("C-c p _"  . cape-tex)
         ("C-c p ^"  . cape-tex)
         ("C-c p &"  . cape-sgml)
         ("C-c p r"  . cape-rfc1345))
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  ;; NOTE: The order matters!
  (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-dict)
  ;;(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-symbol)
  ;;(add-to-list 'completion-at-point-functions #'cape-line)
  )

Use embark for in-mini-buffer actions:

(use-package embark
  :straight t
  :defer t
  :bind (("C-." . embark-act))
  :config
  ())

(use-package embark-consult
  :straight t
  :defer t
  :hook (embark-collect-mode . consult-preview-at-point-mode))

Use corfu-prescient to sort completion candidates:

(use-package corfu-prescient
  :straight t
  :after prescient
  :after corfu
  ;; :hook (corfu-mode . corfu-prescient-mode)
  ;; :hook (text-mode . corfu-prescient-mode)
  :config
  (setq corfu-prescient-completion-styles '(prescient orderless basic partial-completion))
  (corfu-prescient-mode 1)
  )

(defun nemo/reset-corfu-prescient ()
  (interactive)
  (corfu-prescient-mode -1)
  (corfu-prescient-mode 1)
  )

Orderless & Prescient

Install prescient package:

(use-package prescient
  :straight t
  :config
  (add-to-list 'completion-styles 'prescient)
  (add-to-list 'prescient-filter-method 'fuzzy))

Fuzzy and out-of-order completion matching using orderless:

(use-package orderless
  :straight t
  :config
  (add-to-list 'completion-styles 'orderless)
  (setq orderless-matching-styles '(orderless-flex)
        completion-category-overrides '((file (styles basic partial-completion)))
        orderless-component-separator "[ |]"))

Programming

Enable hs-minor-mode in prog-mode to allow for folding away comments and modules/namespaces:

(add-hook 'prog-mode-hook 'hs-minor-mode)

hl-todo

(use-package hl-todo
  :straight t
  :hook ((prog-mode) . hl-todo-mode)
  :config
  (setq hl-todo-keyword-faces
        '(("TODO" . "#e78a4e")))
  )

Copilot 😭

I feel so bad for this.. but its not awful..

(use-package plz
  :straight t)

(require 'ollama-complete)
(defgroup ollama-complete nil "Ollama Code Completion." :group 'completion)

(require 'plz)

(defcustom ollama-complete-host "http://localhost:11434"
  "URL of local ollama service."
  :type 'url)

(defcustom ollama-complete-model "qwen2.5-coder:3b-instruct-q4_K_M"
  "Model to use for Fill-in-Middle completion.")

(defvar ollama-complete--options
  '(("temperature" . 0) ("top_p" . 0.5) ("num_predict" . 30)))

(defvar ollama-complete--overlay nil)

(defvar ollama-complete--overlay-buffer nil
    "Buffer in which the overlay is placed.")

(defun ollama-complete--fill-in-middle ()
  "Ask ollama to generate response for fill-in-middle prompt."
  (interactive)
  (let ((prompt
         (concat
          "<|fim_prefix|>"
          (concat
           (buffer-substring-no-properties (point-min) (point))
           (concat
            "<|fim_suffix|>"
            (concat
             (buffer-substring-no-properties (point) (point-max))
             "<|fim_middle|>"))))))
    (setq ollama-complete--overlay-buffer (current-buffer))
    (message "overlay-buffer: %s" ollama-complete--overlay-buffer)
    (plz 'post
      (concat ollama-complete-host "/api/generate")
      :body (json-encode `(("model" . "qwen2.5-coder:3b-instruct-q4_K_M")
                           ("raw" . t)
                           ("stream" . :json-false)
                           ("options" . ,ollama-complete--options)
                           ("prompt" . ,prompt)
                           ))
      :as #'json-read
      :then (lambda (alist)
              (message "Ollama: FIM Completion: %s" (alist-get 'response alist))
              (ollama-complete--make-overlay (alist-get 'response alist))))
    )
  )

(defun ollama-complete--make-overlay (string)
  "make overlay showign STRING at POS, or move existing overlay there."
  (interactive)
  (if ollama-complete--overlay-buffer
      (with-current-buffer ollama-complete--overlay-buffer
        (message "make-overlay %d \"%s\" %s" (point) string ollama-complete--overlay-buffer)
        (if ollama-complete--overlay
            (move-overlay ollama-complete--overlay (point) (point) ollama-complete--overlay-buffer)
          (setq ollama-complete--overlay (make-overlay (point) (point) ollama-complete--overlay-buffer))
          (overlay-put ollama-complete--overlay 'window (selected-window))
          (overlay-put ollama-complete--overlay
                       'after-string (propertize string 'face 'font-lock-comment-face))
          (add-text-properties 0 1 '(cursor 1) string)
          ollama-complete--overlay)))
  )

(defun ollama-complete--hide-overlay ()
  "Hide temporary completion candidate overlay."
  (interactive)
  (when ollama-complete--overlay
    (delete-overlay ollama-complete--overlay)
    (setq ollama-complete--overlay nil)
    (setq ollama-complete--overlay-buffer nil))
  )

(defun ollama-complete--insert-completion ()
  "Hide temporary completion candidate overlay."
  (interactive)
  (when ollama-complete--overlay
    (let ((completion (overlay-get ollama-complete--overlay 'after-string)))
      (set-text-properties 0 (length completion) nil completion)
        (goto-char (overlay-start ollama-complete--overlay))
        (insert (substring-no-properties completion)))
    (ollama-complete--hide-overlay)))

(provide 'ollama-complete)

smart-parens

(use-package smartparens
  :straight t
  :hook ((prog-mode text-mode markdown-mode) . smartparens-mode)
  :config
  (require 'smartparens-config))

Flycheck

(use-package flycheck
  :straight t
  :defer t
  :hook (after-init. global-flycheck-mode)
  :config)

Yasnippet

Auto-completion requires yasnippet for some competions, such as function arguments and parens.

(use-package yasnippet
  :straight t
  :defer t
  :hook ((prog-mode text-mode) . yas-minor-mode))

(use-package yasnippet-snippets
  :straight t)

LSP

Use lsp-mode and lsp-ui for LSP functionality. Emacs has its own internal LSP client called eglot, but I've never used it and I'm relatively happy with lsp-mode. LSP sets it's prefix key to s-l by default, which uses the Super key which I use as my Mod key in sway, so I can't use it in emacs.

(use-package lsp-mode
  :straight t
  :defer t
  :hook (prog-mode . lsp)
  :hook (lsp-mode . lsp-enable-which-key-integration)
  :hook (lsp-mode . lsp-inlay-hints-mode)
  :init
  (setq lsp-keymap-prefix "C-l")
  :commands lsp
  :general-config
  (leader-def
    "l" '(:keymap lsp-command-map :wk "LSP")
    )
  :config
  (setq
   lsp-idle-delay 0.5
   lsp-inlay-hint-enable t
   lsp-modeline-code-actions-enable t
   lsp-modeline-code-actions-segments '(name count)
   lsp-modeline-diagnostics-enable t
   lsp-completion-enable-additional-text-edit t

   lsp-rust-server 'rust-analyzer
   lsp-rust-analyzer-server-display-inlay-hints t
   lsp-rust-analyzer-proc-macro-enable t
   lsp-rust-analyzer-completion-auto-import-enable t
   lsp-rust-analyzer-proc-macro-enable t
   lsp-rust-analyzer-binding-mode-hints t
   lsp-rust-analyzer-display-closure-return-type-hints t
   lsp-rust-analyzer-server-format-inlay-hints t)

   (setq lsp-glsl-executable '("glsl_analyzer"))
   (add-to-list 'lsp-language-id-configuration '("CMakeLists.txt" . "cmake"))
   )

(use-package consult-lsp
  :straight t
  :general
  (with-eval-after-load 'lsp-mode
  (general-define-key
   :keymaps 'lsp-command-map
   "c s" '("Symbols" . consult-lsp-symbols)
   "c f" '("Symbols" . consult-lsp-file-symbols)
   "c d" '("Diagnostics" . consult-lsp-diagnostics)
   )
    )
  )

(use-package lsp-treemacs
  :straight t
  :commands lsp-treemacs-errors-list)

(use-package lsp-ui
  :config
  (setq
   ;; lsp-modeline-code-actions-enable nil
   ;; lsp-rust-analyzer-cargo-all-targets nil
   ;; lsp-rust-analyzer-server-command "xwin-env rust-analyzer"
   lsp-ui-sideline-show-code-actions t
   lsp-ui-doc-enable t
   lsp-ui-doc-delay 0.5
   lsp-ui-doc-show-with-cursor t
                                        ; lsp-ui-doc-use-childframe t
                                        ; lsp-ui-doc-use-webkit t
   )
  :commands lsp-ui-mode)

(defvar-local nemo/lsp-format-on-save t)

(define-minor-mode nemo/lsp-format-on-save-mode
  "Run lsp-format-buffer on save."
  :lighter " fmt"
  (if nemo/lsp-format-on-save-mode
      (add-hook 'before-save-hook #'lsp-format-buffer nil t)
      (remove-hook 'before-save-hook #'lsp-format-buffer nil t)
    ))

(defun nemo/do-lsp-format-on-save ()
  "Format on save using LSP server."
  (if nemo/lsp-format-on-save
      (lsp-format-buffer)))

Graphics

glsl-mode:

(use-package glsl-mode
  :straight t
  :defer t
  :init
  (add-to-list 'auto-mode-alist '("\\.vert\\'" . glsl-mode))
  (add-to-list 'auto-mode-alist '("\\.glsl\\'" . glsl-mode))
  (add-to-list 'auto-mode-alist '("\\.frag\\'" . glsl-mode))
  )

hlsl-mode:

(require 'hlsl-mode)
(with-eval-after-load 'hlsl-mode
  (add-to-list 'auto-mode-alist '("\\.hlsl\\'" . hlsl-mode))
  (add-to-list 'auto-mode-alist '("\\.frag\\'" . hlsl-mode))
  )
(require 'treesit)

(add-to-list
 'treesit-language-source-alist
 '(slang "https://github.com/tree-sitter-grammars/tree-sitter-slang"))

(with-eval-after-load 'lsp-mode
  (progn
    ;; tree-sitter

    ;; LSP
    (add-to-list 'lsp-language-id-configuration '(".*\\.slang$" . "slang"))
    (lsp-register-client (make-lsp-client
                          :new-connection (lsp-stdio-connection "/opt/shader-slang-bin/bin/slangd")
                          :activation-fn (lsp-activate-on "slang")
                          :server-id 'slang))
    ))

(require 'c-ts-common)
(require 'c-ts-mode)

(defgroup slang nil "Khronos Slang Major Mode."
  :group 'languages)

(defcustom slang-ts-mode-indent-offset 4
  "Number of spaces for each indentation step in `slang-ts-mode'."
  :version "29.1"
  :type 'integer
  :safe 'integerp
  :group 'slang)

(declare-function treesit-parser-create "treesit.c")
(declare-function treesit-node-parent "treesit.c")
(declare-function treesit-node-start "treesit.c")
(declare-function treesit-node-end "treesit.c")
(declare-function treesit-node-child "treesit.c")
(declare-function treesit-node-child-by-field-name "treesit.c")
(declare-function treesit-node-type "treesit.c")
(declare-function treesit-node-prev-sibling "treesit.c")
(declare-function treesit-node-first-child-for-pos "treesit.c")
(declare-function treesit-node-next-sibling "treesit.c")
(declare-function treesit-parser-set-included-ranges "treesit.c")
(declare-function treesit-query-compile "treesit.c")

;; taken from rust-ts-mode
(defvar slang-ts-mode--syntax-table
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?+   "."      table)
    (modify-syntax-entry ?-   "."      table)
    (modify-syntax-entry ?=   "."      table)
    (modify-syntax-entry ?%   "."      table)
    (modify-syntax-entry ?&   "."      table)
    (modify-syntax-entry ?|   "."      table)
    (modify-syntax-entry ?^   "."      table)
    (modify-syntax-entry ?!   "."      table)
    (modify-syntax-entry ?@   "."      table)
    (modify-syntax-entry ?~   "."      table)
    (modify-syntax-entry ?<   "."      table)
    (modify-syntax-entry ?>   "."      table)
    (modify-syntax-entry ?/   ". 124b" table)
    (modify-syntax-entry ?*   ". 23"   table)
    (modify-syntax-entry ?\n  "> b"    table)
    (modify-syntax-entry ?\^m "> b"    table)
    table)
  "Syntax table for `slang-ts-mode'.")

(defvar slang-ts-mode--indent-rules
  `((slang
     ((parent-is "translation_unit") column-0 0)
     ((node-is ")") parent-bol 0)
     ((node-is "]") parent-bol 0)
     ((node-is "}") (and parent parent-bol) 0)
     ((and (parent-is "comment") c-ts-common-looking-at-star)
      c-ts-common-comment-start-after-first-star -1)
     ((parent-is "comment") prev-adaptive-prefix 0)
     ((parent-is "parameter_list") parent-bol slang-ts-mode-indent-offset)
     ((parent-is "binary_expression") parent-bol slang-ts-mode-indent-offset)
     ((parent-is "compount_statement") parent-bol slang-ts-mode-indent-offset)
     ((parent-is "field_declaration_list") parent-bol slang-ts-mode-indent-offset)
     ))
  "Tree-sitter indent rules for `slang-ts-mode'.")

;; taken from rust-ts-mode
(defun slang-ts-mode--syntax-propertize (beg end)
  "Apply syntax properties to special characters between BEG and END.

Apply syntax properties to various special characters with
contextual meaning between BEG and END.

The apostrophe \\=' should be treated as string when used for char literals.

< and > are usually punctuation, e.g., as greater/less-than.  But
when used for types, they should be considered pairs.

This function checks for < and > in the changed RANGES and apply
appropriate text property to alter the syntax of template
delimiters < and >'s."
  (goto-char beg)
  (while (search-forward "'" end t)
    (when (string-equal "char_literal"
                        (treesit-node-type
                         (treesit-node-at (match-beginning 0))))
      (put-text-property (match-beginning 0) (match-end 0)
                         'syntax-table (string-to-syntax "\""))))
  (goto-char beg)
  (while (re-search-forward (rx (or "<" ">")) end t)
    (pcase (treesit-node-type
            (treesit-node-parent
             (treesit-node-at (match-beginning 0))))
      ("template_argument_list"
       (put-text-property (match-beginning 0)
                          (match-end 0)
                          'syntax-table
                          (pcase (char-before)
                            (?< '(4 . ?>))
                            (?> '(5 . ?<))))))))

;; (treesit-query-validate 'slang "(bitfield_clause (identifier) @font-lock-builtin-face) (:match ,(rx bos (or ,@slang-ts-mode--locations) eos))")

(defvar slang-ts-mode--locations
  '("SV_Position" "COLOR" "POSITION" "SV_Target")
  "Slang built-in location hints for font-locking.")

(defvar slang-ts-mode--keywords
  '("struct" "interface" "where" "return")
  "Slang built-in location hints for font-locking.")

(defvar slang-ts-mode--types
  `(,@(mapcar (lambda (type)
                  (concat type "\\([1234]?\\|\\([1234]x[1234]\\)?\\)"))
                '("bool" "int" "uint" "float"
                  "min16float" "min10float" "min16int" "min12int" "min16uint")))
  "Slang types for font-locking.")

(defvar slang-ts-mode--operators
  '("!"  "!=" "%" "%=" "&" "&=" "&&" "*" "*=" "+" "+=" "," "-" "-="
    "->" "." "/" "/=" ":" ";" "<<" "<<=" "<" "<="
    "=" "==" "=>" ">" ">=" ">>" ">>=" "^" "^=" "|" "|=" "||")
  "Slang operators for tree-sitter font-locking.")

(defvar slang-ts-mode--font-lock-settings
  (treesit-font-lock-rules
   :language 'slang
   :feature 'bracket
   '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face)

   :language 'slang
   :feature 'comment
   '(((comment) @font-lock-comment-face))

   :language 'slang
   :feature 'builtin
   '((bitfield_clause (identifier) @font-lock-builtin-face)
     (semantics (identifier) @font-lock-builtin-face))

   :language 'slang
   :feature 'keyword
   `([,@slang-ts-mode--keywords] @font-lock-keyword-face)

   :language 'slang
   :feature 'type
   `(
     ((call_expression function: (identifier) @font-lock-type-face)
     (:match ,(rx bos
                  (and (or "bool" "int" "uint" "float")
                       (or
                        (? (any "1" "2" "3" "4"))
                        (? (and (any "1" "2" "3" "4") "x" (any "1" "2" "3" "4")))))
                  eos) @font-lock-type-face))
     ((call_expression function: (identifier) @font-lock-function-call-face))
     (type_identifier) @font-lock-type-face)

   :language 'slang
   :feature 'definition
   '(
     (declaration declarator: (identifier) @font-lock-property-name-face)
     (field_declaration declarator: (field_identifier) @font-lock-property-name-face)
     (parameter_declaration declarator: (identifier) @font-lock-property-name-face)
     (function_declarator declarator: (identifier) @font-lock-function-name-face)
     (function_declarator declarator: (template_function name: (identifier) @font-lock-type-face))
     (hlsl_attribute (call_expression function: (identifier) @font-lock-builtin-face))
     )

   :language 'slang
   :feature 'function
   '((call_expression function: (identifier) @font-lock-function-call-face)
     (call_expression function: (_ field: (field_identifier) @font-lock-function-call-face))
     )

   :language 'slang
   :feature 'string
   '(((string_content) @font-lock-string-face))

   :language 'slang
   :feature 'type
   '(((type_identifier) @font-lock-type-face)
     (function_definition type: (type_identifier) @font-lock-type-face)
     (declaration type: (type_identifier) @font-lock-type-face)
     (field_declaration type: (type_identifier) @font-lock-type-face)
     (parameter_declaration type: (type_identifier) @font-lock-type-face)
     (template_argument_list (type_descriptor type: (type_identifier) @font-lock-type-face))
     )

   :language 'slang
   :feature 'operator
   `([,@slang-ts-mode--operators] @font-lock-operator-face)

   :language 'slang
   :feature 'variable
   '(((identifier) @font-lock-property-use-face))

   :language 'slang
   :feature 'number
   '(((number_literal) @font-lock-number-face))

   :language 'slang
   :feature 'property
   '(((field_identifier) @font-lock-property-use-face))
   ))

(define-derived-mode slang-ts-mode prog-mode "Slang"
  "Major mode for editing Slang, powered by tree-sitter."
  :group 'slang
  :syntax-table slang-ts-mode--syntax-table

  (when (treesit-ready-p 'slang)
    (treesit-parser-create 'slang)
    (setq-local treesit-font-lock-feature-list
                '((comment definition)
                  (keyword string)
                  (assignment attribute builtin variable constant escape-sequence number definition property type)
                  (bracket delimiter error function operator property variable)
                  ))
    (setq-local syntax-propertize-function #'slang-ts-mode--syntax-propertize
                indent-tabs-mode nil
                treesit-font-lock-settings slang-ts-mode--font-lock-settings
                treesit-simple-indent-rules slang-ts-mode--indent-rules)
    (treesit-major-mode-setup))
  )

(if (treesit-ready-p 'slang)
    (add-to-list 'auto-mode-alist '("\\.slang\\'" . slang-ts-mode)))

(provide 'slang-ts-mode)

Rust

rust-mode

;; (use-package rust-mode
;;   :straight t
;;   ;; :hook (rust-mode . lsp-mode)
;;   :init
;;   (setq rust-mode-treesitter-derive t
;;         ))

rustic-mode

(use-package rustic
  :straight t
  :defer t
  ;; :after (rust-mode)
  :hook(rustic-mode . nemo/lsp-format-on-save-mode)
  :general-config
  (general-define-key
   :prefix-map 'nemo/rust-map
   "c v" '("Check" . rustic-cargo-check)
   "c c" '("Compile" . rustic-compile)
   "c b" '("Build" . rustic-cargo-build)
   ;;"c B" '("Build" . rust-cargo-relea)
   "c c" '("Clean" . rustic-cargo-clean)
   "c x" '("Run" . rustic-cargo-run)
   "c f" '("Format Buffer" . rustic-format-buffer)
   "t t" '("Current Test" . rustic-cargo-current-test)
   "t a" '("Test" . rustic-cargo-test)
   "c a" '("cargo-add" . rustic-cargo-add)
   "c r" '("cargo-rm" . rustic-cargo-remove)
   "c l" '("lint" . rustic-cargo-clippy)
   "c L" '("lint" . rustic-cargo-clippy-fix)
   "c n" '("new" . rustic-cargo-new)
   "c i" '("init" . rustic-cargo-init)
   "c d" '("Docs" . rustic-cargo-doc)
   )
  (leader-major-def
    :keymaps 'rustic-mode-map
    "m" '(:keymap nemo/rust-map :wk "Cargo")
    )
  :config
  (setq rust-mode-treesitter-derive t
        rustic-format-on-save nil
        ;; rustic-format-trigger 'on-save
        ;; rustic-format-on-save-method #'rustic-format-buffer
        rustic-analyzer-command '("/usr/bin/rust-analyzer")
        )
  ;; (setq nemo/lsp-format-on-save t)
  )
(use-package rust-playground
  :straight t
  :defer t)

Debugging

(use-package dap-mode
  :straight t
  :after (lsp-mode)
  :config
  ;; (require 'dap-cpptools)
  ;; (dap-cpptools-setup)
  (require 'dap-gdb-lldb)
  (dap-register-debug-template "Rust::GDB Run Configuration"
                               (list :type "gdb"
                                     :request "launch"
                                     :name "GDB::Run"
                                     :gdbpath "rust-gdb"
                                     :target nil
                                     :cwd nil))
  (dap-auto-configure-mode))

Web

(use-package web-mode
  :straight t
  :defer t
  :mode
  (("\\.phtml\\'"      . web-mode)
   ("\\.tpl\\.php\\'"  . web-mode)
   ("\\.twig\\'"       . web-mode)
   ("\\.xml\\'"        . web-mode)
   ("\\.html\\'"       . web-mode)
   ("\\.htm\\'"        . web-mode)
   ("\\.[gj]sp\\'"     . web-mode)
   ("\\.as[cp]x?\\'"   . web-mode)
   ("\\.eex\\'"        . web-mode)
   ("\\.erb\\'"        . web-mode)
   ("\\.mustache\\'"   . web-mode)
   ("\\.handlebars\\'" . web-mode)
   ("\\.hbs\\'"        . web-mode)
   ("\\.eco\\'"        . web-mode)
   ("\\.ejs\\'"        . web-mode)
   ("\\.svelte\\'"     . web-mode)
   ("\\.ctp\\'"        . web-mode)
   ("\\.djhtml\\'"     . web-mode))
  :config
  (setq
   web-mode-css-indent-offset 2
   web-mode-code-indent-offset 2
   web-mode-attr-indent-offset 2
   web-mode-markup-indent-offset 2))

C/C++

clang-format

(use-package clang-format
  :straight t
  :defer t)
(use-package cmake-mode
  :straight t)

Zig

Use zls automatically with lsp, and zig-mode:

(use-package zig-mode
  :straight t
  :config
  (add-to-list 'auto-mode-alist '("\\.zig\\'" . zig-mode))
  (setq lsp-zig-enable-build-on-save t
        lsp-zig-enable-inlay-hints nil
        lsp-zig-build-on-save-step "check"))

User Functions

PascalCase to snake_case:

(defun snake-case ()
  "convert region from PascalCase into snake_case"
  (interactive)
       (save-excursion
         (let ((bounds (bounds-of-thing-at-point 'symbol)))
           (replace-regexp "\\([A-Z]\\)" "_\\1" nil (1+ (car bounds)) (cdr bounds))
           (downcase-region (car bounds) (cdr (bounds-of-thing-at-point 'word)))))
       )

Shortcut for opening this file:

(defun nemo/edit-init-org ()
  "Open the init.org file."
  (interactive)
  (find-file (concat user-emacs-directory "/init.org"))
  )

Dotfiles with magit:

(defun nemo/magit-dotfiles ()
  "View Dotfiles repository with magit"
  (interactive)
  (require 'magit)
  (let ((magit-git-global-arguments
         `(,(substitute-env-vars "--git-dir=$HOME/.cfg")
           ,(substitute-env-vars "--work-tree=$HOME")
           ,@magit-git-global-arguments)))
    (magit-status "~")
    (recursive-edit)))

Open the current file with a sudo tramp:

(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
(require 'tramp)

(defun sudo-edit-current-file ()
  (interactive)
  (let ((position (point)))
    (find-alternate-file
     (if (file-remote-p (buffer-file-name))
         (let ((vec (tramp-dissect-file-name (buffer-file-name))))
           (tramp-make-tramp-file-name
            "sudo"
            (tramp-file-name-user vec)
            (tramp-file-name-host vec)
            (tramp-file-name-localname vec)))
       (concat "/sudo:root@localhost:" (buffer-file-name))))
    (goto-char position)))
(leader-def "fe" 'sudo-edit-current-file)

(defun nemo/switch-to-last-buffer ()
  (interactive)
  (switch-to-buffer nil)
  )

Keybinds

(define-key custom-bindings-map (kbd "C-c l") 'org-store-link)
(define-key custom-bindings-map (kbd "C-c a") 'org-agenda)
(define-key custom-bindings-map (kbd "C-c c") 'org-capture)
(define-key custom-bindings-map (kbd "C-c t") 'org-todo)
(define-key custom-bindings-map (kbd "M-h") 'org-todo)