.emacs.d

My .emacs.d configuration. Even have a Github Actions pipeline

Stars
8

[[https://github.com/themkat/.emacs.d/actions/workflows/build.yaml][file:https://github.com/themkat/.emacs.d/actions/workflows/build.yaml/badge.svg]]

  • .emacs configuration
    If you are reading this, I finally got around to writing my .emacs config using Org mode. Why? Because it makes is easy to document the whys of my config. I also think it will make it fun to re-read my comments and text after some time :P

The configuration will change with time, based upon programming languages used and so on. For some languages I just use try to use them in the current session (see below), because I use them so rarely :P

When starting my configuration, it will look like the following (see below for configuration of dashboard, modeline etc. :) ):

#+ATTR_ORG: :width 600 [[./screenshot.png]]

** Table of contents :TOC_4:

  • [[#emacs-configuration][.emacs configuration]]
    • [[#basic-setup][Basic setup]]
    • [[#keep-packages-up-to-date][Keep packages up-to-date]]
    • [[#exwm---emacs-window-manager][EXWM - Emacs Window manager]]
    • [[#behaviorial-settings][Behaviorial settings]]
      • [[#general][general]]
      • [[#key-help][Key help]]
      • [[#helm][helm]]
      • [[#navigating-windows-better][Navigating windows better]]
      • [[#installing-system-packages-apt-homebrew-etc][Installing system packages (apt, homebrew etc.)]]
      • [[#dashboard][dashboard]]
      • [[#themes-and-ui][themes and ui]]
      • [[#documentation-viewers][Documentation viewers]]
      • [[#try][try]]
    • [[#editing--settings][Editing settings]]
      • [[#general-editing][General editing]]
      • [[#spell-checking][Spell checking]]
      • [[#completion-general][Completion general]]
      • [[#ide-functionality---general][IDE functionality - general]]
      • [[#programming--scripting--markup-languages-and-so-on][Programming-, scripting-, markup-languages and so on]]
        • [[#c][C]]
        • [[#assembly-various-flavors][Assembly (various flavors)]]
        • [[#rust][Rust]]
        • [[#java][Java]]
        • [[#kotlin][Kotlin]]
        • [[#typescript][TypeScript]]
        • [[#web-development-html-css-basic-js-etc][Web development (HTML, CSS, basic JS etc.)]]
        • [[#python][Python]]
        • [[#scheme][Scheme]]
        • [[#dockerfiles][Dockerfiles]]
        • [[#gherkin-like-feature-files][Gherkin-like feature files]]
        • [[#markdown][Markdown]]
        • [[#yaml][YAML]]
        • [[#xml][XML]]
        • [[#wgsl---webgpu-shading-language][WGSL - WebGPU Shading Language]]
        • [[#zig][Zig]]
      • [[#git-and-project-handling][git and project handling]]
      • [[#sidebar-tree-navigation][Sidebar tree navigation]]
      • [[#better-terminals][Better terminals]]
      • [[#writing-booksorg-mode-etc][Writing (books/org-mode etc.)]]

** Basic setup Before we begin, we have to do some basic setup. For the rest of the config, I will use =use-package= to install packages. I want newer packages, and packages not available in the gnu repos, so adding MELPA and the org repo (to get newest org mode). Then we install =use-package= and set te always ensure variable to make sure it always tries to download the packages we want.

#+BEGIN_SRC emacs-lisp ;;; -- lexical-binding: t; -- ;; sets lexical bindings so we get real closres with let etc. like in Scheme

;; TODO: do we need both melpa and melpa-stable. Isn't melpa enough? (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/")) (add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t) ;; for newest version of org mode (package-initialize)

;; only refresh package contents if we havent downloaded it yet. (unless package-archive-contents (package-refresh-contents))

(unless (package-installed-p 'use-package) (package-install 'use-package))

;; always download packages when we don't have them locally (require 'use-package) (setq use-package-always-ensure 't) #+END_SRC

Some packages are also not in a package manager, and I may want to install it directly from a repo. Straight is good for this type of scenario! #+BEGIN_SRC emacs-lisp (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))

;; Avoid straight.el fucking with org-mode (use-package org :straight (:type built-in)) #+END_SRC

** Keep packages up-to-date I'm a bit lazy when it comes to updating packages manually, so automating this is a welcome addition :)

#+BEGIN_SRC emacs-lisp (use-package auto-package-update :custom (auto-package-update-interval 31) (auto-package-update-delete-old-versions t) (auto-package-update-prompt-before-update t) (auto-package-update-show-preview t)

:config
(auto-package-update-maybe))

#+END_SRC

** EXWM - Emacs Window manager On my GNU/Linux machines I have used EXWM as a window manager for quite a while. It makes navigating windows easier, and makes it possible to do everything in Emacs <3 To set it up, I set up a custom X-session with a .xinitrc file for my user:

#+BEGIN_SRC bash :tangle "no"

various settings like sound applets depending on distro and tooling

examples include nm-applet, xfsettingsd & etc. to be able to piggyback on some xcfe tooling etc.

define this environment variable to enable tangling and running of exwm config

export USE_EXWM="yes" exec dbus-launch --exit-with-session emacs #+END_SRC

Then on launch it will run the following setup for using EXWM:

#+BEGIN_SRC emacs-lisp :tangle (if (getenv "USE_EXWM") "yes" "no") (defun themkat/setup-exwm () ;; Shrink fringes to 1 pixel (fringe-mode 1)

;; Display the time in the modeline
(setq display-time-default-load-average nil)
(setq display-time-day-and-date t display-time-24hr-format t)
(display-time-mode t)

;; Emacs server is not required to run EXWM but it has some interesting uses
;; (see next section)
(server-start)

;; Load EXWM
(require 'exwm)

;; Set the initial number of workspaces.
(setq exwm-workspace-number 2)

;; Buffer names for EXWM
(add-hook 'exwm-update-class-hook
          (lambda ()
            (unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                        (string= "gimp" exwm-instance-name))
              (exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
          (lambda ()
            (when (or (not exwm-instance-name)
                      (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                      (string= "gimp" exwm-instance-name))
              (exwm-workspace-rename-buffer exwm-title))))


;; + Bind "s-0" to "s-3" to switch to the corresponding workspace.
(dotimes (i 4)
  (exwm-input-set-key (kbd (format "s-%d" i))
                      `(lambda ()
                         (interactive)
                         (exwm-workspace-switch-create ,i)
                         (message (concat "Switched to workspace: "
                                          (number-to-string ,i))))))

;; + Application launcher
(exwm-input-set-key (kbd "s-&")
                    (lambda (command)
                      (interactive (list (read-shell-command "$ ")))
                      (start-process-shell-command command nil command)))


;; quickly switch between line and char modes
(exwm-input-set-key (kbd "s-o") #'exwm-input-toggle-keyboard)

;; quickly change keyboard layout
(let ((currLayout "no"))
  (exwm-input-set-key (kbd "s-k")
                      (lambda ()
                        (interactive)
                        (setq currLayout (if (string-equal currLayout "no") "us" "no"))
                        (start-process-shell-command ""
                                                     nil
                                                     (concat "setxkbmap -layout " currLayout))
                        (message (concat "Changed keyboard layout to: " currLayout)))))

;; system tray for Dropbox, Skype volume control, wireless manager etc.
(require 'exwm-systemtray)
(exwm-systemtray-enable)


;; turn on multimonitor support
;; TODO: probably needs to be tuned for each machine as the randr screen identifiers will be different
(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist '(1 "DP-1-1"))
(add-hook 'exwm-randr-screen-change-hook
          (lambda ()
            (start-process-shell-command
             "xrandr" nil "xrandr --output DP-1-1 --right-of eDP-1-1 --auto")))
(exwm-randr-enable)

(exwm-enable))

(if (getenv "USE_EXWM") (use-package exwm :init (themkat/setup-exwm))) #+END_SRC

Desktop environment can also be very useful when Emacs blocks the function keys (volume etc.). I have this issue with EXWM, and desktop-environment fixes it. #+BEGIN_SRC emacs-lisp (use-package desktop-environment :requires exwm :init (desktop-environment-mode)) #+END_SRC

** Behaviorial settings These settings control how Emacs behaves in general (i.e, for all modes); what UI elements to show, theming, what meta-key to use on Mac OS X, Helm to navigatge etc. :)

*** general I use some general settings to make Emacs feel better.

#+BEGIN_SRC emacs-lisp ;; TODO: do these, the clipboard settings belong in editor settings instead?

;; Get PATH from session instead of whatever idiotic things are done before (use-package exec-path-from-shell :init (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize)))

;; set default coding of buffers (setq default-buffer-file-coding-system 'utf-8-unix)

;; switched from tabs to spaces for indentation ;; also set the indentation level to 4. (setq-default indent-tabs-mode nil) (setq-default tab-width 4)

;; Don't autosave. (setq auto-save-default nil)

;; GUI related settings (if (display-graphic-p) (progn ;; Removed annoying UI elements (menu-bar-mode -1) (tool-bar-mode -1) (scroll-bar-mode -1)

    ;; shows battery status (useful when using EXWM)
    (display-battery-mode 1)))

;; disable the C-z sleep/suspend key ;; rarely use emacs in terminal mode anymore and that is the only place it can be useful ;; see http://stackoverflow.com/questions/28202546/hitting-ctrl-z-in-emacs-freezes-everything ;; for a way to have both if I ever want that again. (global-unset-key (kbd "C-z"))

;; disable the C-x C-b key, because I use helm (C-x b) instead (global-unset-key (kbd "C-x C-b"))

(setq display-time-default-load-average nil) (setq display-time-day-and-date t display-time-24hr-format t) (display-time-mode t)

;; make copy and paste use the same clipboard as emacs. (setq select-enable-primary t select-enable-clipboard t)

;; Ensure I can use paste from the Mac OS X clipboard ALWAYS (or close) (when (memq window-system '(mac ns)) (setq interprogram-paste-function (lambda () (shell-command-to-string "pbpaste"))))

;; sets monday to be the first day of the week in calendar (setq calendar-week-start-day 1)

;; save emacs backups in a different directory ;; (some build-systems build automatically all files with a prefix, and .#something.someending breakes that) (setq backup-directory-alist '(("." . "~/.emacsbackups")))

;; Don't create lockfiles. Many build systems that continously monitor the file system get confused by them (e.g, Quarkus). This sometimes causes the build systems to not work anymore before restarting (setq create-lockfiles nil)

;; Enable show-paren-mode (to visualize paranthesis) and make it possible to delete things we have marked (show-paren-mode 1) (delete-selection-mode 1)

;; don't show the emacs splash screen as I use a custom dashboard instead (setq inhibit-startup-screen t)

;; use y or n instead of yes or no (defalias 'yes-or-no-p 'y-or-n-p)

;; Instead of making annoying beeps, blink the modeline on various errors. ;; Taxed/Stolen from: ;; http://whattheemacsd.com/appearance.el-02.html (setq ring-bell-function (lambda () (invert-face 'mode-line) (run-with-timer 0.1 nil 'invert-face 'mode-line)))

;; Make me confirm before exiting Emacs ;; I have become a clumsy piece of shit with my age, and sometimes exit Emacs by mistake (setq confirm-kill-emacs 'yes-or-no-p)

;; Use Monaspace Radon if available ;; https://monaspace.githubnext.com/ ;; (install pls!) (let ((desired-font "--Monaspace Radon-regular-normal-normal--12---*-p-0-iso10646-1")) (unless (null (find-font (font-spec :name desired-font))) (set-frame-font desired-font nil t)))

;; More comfortable smooth scrolling ;; (also showing whats ahead with scroll-margin. That means that it starts smoothly scrolling 15 lines before it reaches top or bottom) (setq scroll-conservatively 10 scroll-margin 15) #+END_SRC

This one only applies to Mac, but makes my life easier. The different brackets became almost impossible to use without this :P Controlling which key is the actual meta key. #+BEGIN_SRC emacs-lisp (setq mac-command-modifier 'meta) (setq mac-option-modifier nil) #+END_SRC

It is nice to open images in Emacs. Make ImageMagick or similar convert images that are not supported natively for me: #+BEGIN_SRC emacs-lisp (setq image-use-external-converter t) #+END_SRC

*** Key help Sometimes I forget a hotkey-sequence I don't use that often, or a better case just remember the beginning of a longer sequence. Then which-key comes in handy! which-key shows possible continuations of a key-sequence. If you type C-x with your keyboard, it will suggest many continuations like C-+, C--, h etc.

#+BEGIN_SRC emacs-lisp (use-package which-key :custom (which-key-idle-delay 5) :config (which-key-mode)) #+END_SRC

*** helm I use helm because i prefer it to ido or alternatives. It is simple to use, has a great UI, and to me it makes Emacs even more powerful as both a text editor and window manager (to switch windows). It will install after projectile (which makes project handling a breeze), which is found with the git and project handling setup [[*git and project handling]]. Here I simply activate it, make the search less rigid (not just beginning of strings, but anywhere in them), remove certain buffers from the buffer list and activate some key bindings globally to do various operations.

#+BEGIN_SRC emacs-lisp (use-package helm :after (projectile) :config (helm-mode 1) (projectile-mode +1) (helm-projectile-on) (helm-adaptive-mode 1) ;; hide uninteresting buffers from buffer list (add-to-list 'helm-boring-buffer-regexp-list (rx "magit-")) (add-to-list 'helm-boring-buffer-regexp-list (rx "*helm"))

:custom
(helm-M-x-fuzzy-match t)
(projectile-completion-system 'helm)
(helm-split-window-in-side-p t)

:bind
(("M-x" . helm-M-x)
 ("C-x C-f" . helm-find-files)
 ;; get the awesome buffer list instead of the standard stuff
 ("C-x b" . helm-mini)))

#+END_SRC

*** Navigating windows better For years I navigated windows in Emacs with =C-x o= because of habit. Not anymore! After starting using the Sway window manager, I realized how tedious it was. You can say that Sway cured my Stockholm syndrome, and now I can navigate with =M-= to navigate to buffer windows based on position.

#+BEGIN_SRC emacs-lisp (use-package windmove :ensure nil :bind* (("M-" . windmove-left) ("M-" . windmove-right) ("M-" . windmove-up) ("M-" . windmove-down))) #+END_SRC

*** Installing system packages (apt, homebrew etc.) TODO: Check if this should be somewhere else in the config Having searches for system packages and installations directly in Emacs is pretty neat!

#+BEGIN_SRC emacs-lisp (use-package helm-system-packages) #+END_SRC

*** dashboard Emacs is always open at my machine, so I really enjoy a friendly startup screen :) dashboard provides what I want with projects (from projectiles list), recently edited files and latest news from Hackernews. To make the experience even better I also install all-the-icons to get pretty icons. NOTE: At first run, you should run =M-x all-the-icons-install-fonts= to get the fonts needed for the icons to show properly.

#+BEGIN_SRC emacs-lisp ;; Getting pretty icons (use-package all-the-icons)

(use-package dashboard :after (all-the-icons dashboard-hackernews helm-system-packages) :init (dashboard-setup-startup-hook)

;; seems like the latest versions do some fuckery with the project list or something.
;; Need an extra refresh after initialization for my own settings to show up now.
;; (did not need this before. Would rather keep the :custom block instead of setq spamming)
:hook
(dashboard-after-initialize . dashboard-refresh-buffer)

:custom
(dashboard-banner-logo-title "Welcome my queen! Make some kewl stuff today!")
(dashboard-startup-banner 'logo)
(dashboard-center-content t)
(dashboard-set-navigator t)
(dashboard-navigator-buttons '((("⤓" " Install system package" " Install system package" (lambda (&rest _) (helm-system-packages))))))
(dashboard-icon-type 'all-the-icons)
;; TODO: enable again when they work
;;       https://github.com/emacs-dashboard/emacs-dashboard/issues/459
(dashboard-set-heading-icons nil)
(dashboard-set-file-icons t)
(dashboard-projects-backend 'projectile)
(dashboard-items '((projects . 5)
                   (recents . 5)
                   (hackernews . 5))))

(use-package dashboard-hackernews) #+END_SRC

*** themes and ui To make Emacs better looking, I use the leuven-theme. This theme improves org-mode readability and makes Emacs blue and pretty in general :) I used to use doom-themes, moe-themes and so on with a simple theme switcher function, but I mostly just use leuven so I decided to remove them. The modeline is made prettier and more modern with doom modeline to get a beautiful powerline :)

#+BEGIN_SRC emacs-lisp (use-package leuven-theme)

;; just to make sure dash, f and s are present. (use-package dash) (use-package s) (use-package f) ;; TODO: use-package here eventually (eval-after-load 's (progn (require 's) (add-to-list 'custom-theme-load-path (s-concat user-emacs-directory "themes/pink-bliss-uwu")) (load-theme 'pink-bliss-uwu 't)))

(use-package doom-modeline :init (doom-modeline-mode 1)) #+END_SRC

Recently I also started using tabs with the centaur-tabs package: #+BEGIN_SRC emacs-lisp ;; Unset the default behavior of the C-x and arrow key navigation (global-unset-key (kbd "C-x ")) (global-unset-key (kbd "C-x "))

(use-package centaur-tabs :after (dashboard org)

:config
(centaur-tabs-mode 1)

;; Custom group function to get it the way _I_ want
(defun centaur-tabs-buffer-groups ()
  "Groups tabs based on which project root they are in if possible"
  (let ((get-closest-projectile-project
         (lambda (path)
           (let ((expanded-path (f-long path)))
             (-first (lambda (proj)
                       (s-starts-with? proj
                                       expanded-path))
                     (-map (lambda (proj)
                             (f-long proj))
                           projectile-known-projects))))))
    (list (cond
           ;; Group as part of projectile project if directly part of it
           ((condition-case _err
                (projectile-project-root)
              (error nil))
            (f-expand (projectile-project-root)))
           ;; Try to group as part of projectile project if indirectly part of it (started from the same directory, not yet tracked, or maybe temporary buffer)
           (get-closest-projectile-project default-directory)
           ((string-equal "*" (substring (buffer-name) 0 1))
            "proc-buffers")
           ;; ... other groupings ...
           (t
            "Other")))))

:custom
(centaur-tabs-set-icons t)
(centaur-tabs-plain-icons t)
(centaur-tabs-set-modified-marker t)

:bind
(("C-x <left>" . centaur-tabs-backward-tab)
 ("C-x <right>" . centaur-tabs-forward-tab)
 ("C-z <left>" . centaur-tabs-move-current-tab-to-left)
 ("C-z <right>" . centaur-tabs-move-current-tab-to-right))

;; TODO: EXWM buffers
;; Disable tab bars in the below modes
:hook
((dashboard-mode . centaur-tabs-local-mode)
 (org-src-mode . centaur-tabs-local-mode)
 (calendar-mode . centaur-tabs-local-mode)
 (dap-ui-breakpoints-mode . centaur-tabs-local-mode)
 (dap-ui-sessions-mode . centaur-tabs-local-mode)
 (dap-ui-repl-mode . centaur-tabs-local-mode)
 (lsp-treemacs-generic-mode . centaur-tabs-local-mode)))

#+END_SRC

*** Documentation viewers

man-pages are good, but sometimes I need a shorter version For that, tldr is great! #+BEGIN_SRC emacs-lisp (use-package tldr) #+END_SRC

devdocs are also pretty handy to have, especially pre-downloaded entries I can read directly in Emacs: #+BEGIN_SRC emacs-lisp (use-package devdocs) #+END_SRC NOTE: Remember to download documentation with =devdocs-install= before intended use.

Usage:

  • devdocs-peruse opens a specific documentation (e.g, kotlin-1.9, rust book, Spring Boot etc.)
  • devdocs-lookup lets me see a list of the topics in that specific doc.

*** try Sometimes I like to try packages without having them as a permanent part of my Emacs setup. try does exactly that, where the packages are gone after Emacs is closed.

#+BEGIN_SRC emacs-lisp (use-package try) #+END_SRC

*** goto-line with preview goto-line waits until we hit enter to show us which line we went to. Sometimes I am off by a few digits, and preview helps me move faster.

#+BEGIN_SRC emacs-lisp (use-package goto-line-preview) #+END_SRC

** Editing settings

*** General editing Line numbers #+BEGIN_SRC emacs-lisp (add-to-list 'prog-mode-hook 'display-line-numbers-mode)

(custom-set-faces '(line-number-current-line ((t (:inherit line-number :background "white" :foreground "black"))))) #+END_SRC

Rainbow mode #+BEGIN_SRC emacs-lisp (use-package rainbow-mode :hook prog-mode) #+END_SRC

focus mode!!! Grays out the rest of the buffer, and only highlights the given function we are in. #+BEGIN_SRC emacs-lisp (use-package focus) #+END_SRC

Yasnippet makes boiler plate and other code snippets much faster to write with snippets that activates with small keywords. Just type the keyword and TAB, and yasnippet will fill in the snippet :) (you may have to fill in some names like class name or parameter names after TAB off course...). #+BEGIN_SRC emacs-lisp (use-package yasnippet :config (yas-reload-all)

:hook
(sh-mode . yas-minor-mode))

;; install useful snippets ;; Thought I already had installed these, must have been an older setup I had :P Years pass by so fast (use-package yasnippet-snippets :after yasnippet) #+END_SRC

Sometimes we want to edit multiple places in the file at the same time. Most of the time this is just adding the same characters multiple places in the file in places with the same pattern, other times it is inserting a sequence of numbers. #+BEGIN_SRC emacs-lisp (use-package multiple-cursors :bind ("C->" . mc/mark-next-like-this) ("C-<" . mc/mark-previous-like-this)) #+END_SRC

Paredit makes paranthesis handling a breeze in Lisp-languages :) Only setting I really need is to make it possible to select something and delete the selection (including the paranthesis). #+BEGIN_SRC emacs-lisp (use-package paredit :config ;; making paredit work with delete-selection-mode ;; found on the excellent place called what the emacs d. (put 'paredit-forward-delete 'delete-selection 'supersede) (put 'paredit-backward-delete 'delete-selection 'supersede) (put 'paredit-open-round 'delete-selection t) (put 'paredit-open-square 'delete-selection t) (put 'paredit-doublequote 'delete-selection t) (put 'paredit-newline 'delete-selection t)

  :hook
  ((emacs-lisp-mode . paredit-mode)
   (scheme-mode . paredit-mode)))

#+END_SRC

Certain strings should in my view be translated to unicode symbols, and so far I just set some defaults for all modes. #+BEGIN_SRC emacs-lisp ;; should I defaults? or maybe one for c-like languages, one for lisp etc.? (setq-default prettify-symbols-alist '(("lambda" . 955) ("->" . 8594) ("!=" . 8800))) (global-prettify-symbols-mode) #+END_SRC

Undo-tree. I LOOOOVE undo-tree <3 Instead of having a linear line of operations we can undo and redo, I have a tree I can navigate :D #+BEGIN_SRC emacs-lisp (use-package undo-tree :config (add-to-list 'undo-tree-incompatible-major-modes #'nxml-mode) (global-undo-tree-mode)

:custom
(undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))

#+END_SRC

Emojis in comments, org mode text and other places are really fun and makes the text feel more alive (instead of showing codes for emojis where applicable). (sometimes I turn it off because it ends up emojifying too much, but that is easy with =M-x emojify-mode=). #+BEGIN_SRC emacs-lisp (use-package emojify :init (add-hook 'after-init-hook #'global-emojify-mode)) #+END_SRC

*** Spell checking Acivate spell checking for some relevant modes, set some preferred languages and makes the correction prettier with helm.

#+BEGIN_SRC emacs-lisp ;; FlySpell (spell checking) (dolist (flyspellmodes '(text-mode-hook org-mode-hook latex-mode-hook)) (add-hook flyspellmodes 'turn-on-flyspell))

;; comments and strings in code (add-hook 'prog-mode-hook 'flyspell-prog-mode)

;; sets american english as defult (setq ispell-dictionary "american")

;; let us cycle american english (best written english) and norwegian (defun change-dictionary () (interactive) (ispell-change-dictionary (if (string-equal ispell-current-dictionary "american") "norsk" "american")))

;; helm functionality for flyspell. To make it more user friendly (use-package helm-flyspell :after (helm flyspell) :init ;; Disable standard keys for flyspell correct, and make my own for helm. (define-key flyspell-mode-map (kbd "C-.") nil) (define-key flyspell-mode-map (kbd "C-,") #'helm-flyspell-correct)) #+END_SRC

*** Completion general company (COMPLete ANY) provides base functionality for completions (ui elements, searching for candidates etc). For many modes, company is sufficient, but for some languages it can be great to use with something like lsp-mode to provide more advanced completion (like for Java and Kotlin).

#+BEGIN_SRC emacs-lisp (use-package company :config (global-company-mode)

:custom
;; set the completion to begin at once
(company-idle-delay 0)
(company-echo-delay 0)
(company-minimum-prefix-length 1)

:bind
;; trigger company to see a list of choices even when nothing is typed. maybe it quit because we clicked something. or maybe we dont know what to type yet :P
;; CTRL-ENTER. Because C-RET does not work. 
([(control return)] . company-complete))

;; a nicer way to show company completions with icons and doc popup where available (lsp etc.) ;; Also doesn't clutter up the screen with super-big multiline truncated lines (use-package company-box :after company :if (display-graphic-p) :custom (company-box-frame-behavior 'point) (company-box-show-single-candidate t) (company-box-doc-delay 1)

:hook
(company-mode . company-box-mode))

;; little hack function to make company box frame bigger (defun themkat/company-box-fix-size () (interactive) (let* ((box-frame (company-box--get-frame))) (when (not (null box-frame)) (set-face-attribute 'default box-frame :height 180)))) #+END_SRC

*** IDE functionality - general LSP = Language Server Protocol lsp-mode uses LSP servers to provides IDE functionality like code completion (intellisense like using company-capf), navigation (jump to symbol), refactoring functionality and so on. lsp-ui is used to get prettier boxes and more info visible in an easy way (like javadoc). Currently dap-mode is added because I play a bit with it, and my first impressions are great so far (for the few times I use a debugger, I know I'm weird for not needing it much at all).

#+BEGIN_SRC emacs-lisp (use-package lsp-mode :bind (:map lsp-mode-map ("M-RET" . lsp-execute-code-action))

;; Save automatically on lsp-rename etc. Usually it opens buffers in the background that contains the edits...
;; https://github.com/emacs-lsp/lsp-mode/issues/4087
:hook
(lsp-after-apply-edits . save-buffer))

;; helper boxes and other nice functionality (like javadoc for java) (defun lsp-ui-show-doc-helper () (interactive) (if (lsp-ui-doc--visible-p) (lsp-ui-doc-hide) (lsp-ui-doc-show)))

(use-package lsp-ui :after lsp-mode :custom (lsp-ui-sideline-show-code-actions t) (lsp-ui-doc-position 'at-point) :bind (:map lsp-mode-map ("M-s M-d" . lsp-ui-show-doc-helper)))

;; Additional helpers using treemacs ;; (symbols view, errors, dependencies for Java etc.) (use-package lsp-treemacs :after lsp-mode :config (lsp-treemacs-sync-mode 1))

;; debugger component (for the few times I need it) (use-package dap-mode :after lsp-mode :init (dap-auto-configure-mode)) #+END_SRC

Some modes uses flycheck to provide syntax correctness checks (e.g, red lines below errors). #+BEGIN_SRC emacs-lisp (use-package flycheck :custom (flycheck-indication-mode nil) (flycheck-highlighting-mode 'lines)) #+END_SRC

I also find it useful to be able to see cognitive complexity metrics while coding. #+BEGIN_SRC emacs-lisp (use-package codemetrics :straight (codemetrics :type git :host github :repo "jcs-elpa/codemetrics"))

;; some dependencies ;; (tree-sitter can be used for way more things, but I mainly use it for codemetrics atm) (use-package tree-sitter-langs) #+END_SRC

*** Programming-, scripting-, markup-languages and so on Some languages work great out of the box, some require a little tweaking.

**** C C does not really need much auto completion, but it can be great to have it for projects that use some external libraries (like libogc for Nintendo GameCube development, where you have a SDK for the console). I used to just use company-c-headers and company-clang for this, but realized that some extra popups with documentation comments, error checking, completion etc. was most welcome! clangd is a language server that provides that for C, C++, CUDA C etc. While I REALLY HATE that it doesn't auto include headers for DevkitPro if I don't have them open in a source file in the project, it seems to be more feature rich than CCLS. Documentation shows better, signature help etc. (I'm too fucking old to remember all the headers, import statements etc. in many languages).

Prerequisites: To get all include paths and settings for a project correct, one should create a =compile_commands.json= file that clangd will read. I use [[https://github.com/nickdiego/compiledb][CompileDB]] to generate this file, as it seems to generate a useful file even for projects where tools like Bear have problems. For CMake projects (ugh), one simply adds =-DCMAKE_EXPORT_COMPILE_COMMANDS=YES= to the cmake command.

#+BEGIN_SRC emacs-lisp ;; configure built in here with ensure nil (use-package cc-mode :ensure nil :after (lsp-mode) :hook ((c-mode . lsp) (c-mode . yas-minor-mode))) #+END_SRC

Some projects also use CMake as a build system in the C and C++ world. Handy to have CMake syntax highlighting available: #+BEGIN_SRC emacs-lisp (use-package cmake-mode) #+END_SRC (just activate lsp using M-x in a CMake buffer after this to get completion etc.! Requires installation of [[https://emacs-lsp.github.io/lsp-mode/page/lsp-cmake/][cmake-language-server]])

**** Assembly (various flavors) 6502 Assembly (especially for Commodore 64): #+BEGIN_SRC emacs-lisp (use-package mos-mode) #+END_SRC (my own package lol)

**** Rust Recently started experimenting more with Rust. rustic seems to be the best package for working with it.

#+BEGIN_SRC emacs-lisp (use-package rustic :after (yasnippet)

:custom
(rustic-format-trigger 'on-save)
(rustic-format-on-save-method 'rustic-format-buffer)
(lsp-rust-all-features t)

:hook
(rustic-mode . yas-minor-mode))

#+END_SRC

**** Java lsp-java :drool:

Java IDE-like functionality in Emacs. When we run this mode for the first time, the lsp server will be downloaded automatically. Works like a charm!

#+BEGIN_SRC emacs-lisp (use-package lsp-java :hook (java-mode . lsp)

:bind
(:map java-mode-map 
      ("M-RET" . lsp-java-organize-imports)))

;; Java snippets for yasnippet. Found them very useful so far (use-package java-snippets :after yasnippet :hook (java-mode . yas-minor-mode)) #+END_SRC

**** Kotlin lsp-mode works out of the box with Kotlin mode as long as [[https://github.com/fwcd/kotlin-language-server][kotlin-language-server]] is in the path :) So I only install Kotlin-mode :)

#+BEGIN_SRC emacs-lisp (defun themkat/kotlin-register-debug-templates () ;; various debug templates for Kotlin will be put here (dap-register-debug-template "Kotlin tests with launcher" (list :type "kotlin" :request "launch" :mainClass "org.junit.platform.console.ConsoleLauncher --scan-class-path" :enableJsonLogging nil :noDebug nil)))

(use-package kotlin-mode :after (lsp-mode dap-mode yasnippet) :config (require 'dap-kotlin) ;; should probably have been in dap-kotlin instead of lsp-kotlin (setq lsp-kotlin-debug-adapter-path (or (executable-find "kotlin-debug-adapter") "")) (themkat/kotlin-register-debug-templates) :hook ((kotlin-mode . lsp) (kotlin-mode . yas-minor-mode))) #+END_SRC

I have written briefly on my blog about Kotlin in Emacs. [[https://themkat.net/2021/11/03/kotlin_in_emacs.html][Article 1]] and [[https://themkat.net/2022/09/24/kotlin_in_emacs_redux.html][Article 2]]. They contain some minor tips and tricks, as well as other links that might prove useful.

**** TypeScript #+BEGIN_SRC emacs-lisp ;; Function to activate tide by need (defun themkat/activate-tide () (interactive) (tide-setup) (flycheck-mode 1) (setq flycheck-check-syntax-automatically '(save mode-enabled)) (eldoc-mode 1) (tide-hl-identifier-mode 1))

;; TODO: see if we can replace the web-mode stuff with lsp as well. ;; only used for the mixed content web mode stuff now. (use-package tide :after typescript-mode)

;; typescript-mode used to be included in another package (probably tide?), but not anymore it seems (use-package typescript-mode) #+END_SRC

**** Web development (HTML, CSS, basic JS etc.) Makes it more comfortable to edit mixed files (javascript + html in same document, jsx etc.).

#+BEGIN_SRC emacs-lisp (defun themkat/complete-web-mode () (interactive) (let ((current-scope (web-mode-language-at-pos (point)))) (cond ((string-equal "javascript" current-scope) (company-tide 'interactive)) ((string-equal "css" current-scope) (company-css 'interactive)) (t (company-dabbrev-code 'interactive)))))

(defun themkat/eldoc-web-mode () (let ((current-scope (web-mode-language-at-pos (point)))) (cond ((string-equal "javascript" current-scope) (tide-eldoc-function)) ((string-equal "css" current-scope) (css-eldoc-function)) (t nil))))

(defun themkat/setup-web-mode-mixed () (web-mode) (themkat/activate-tide) (setq-local eldoc-documentation-function #'themkat/eldoc-web-mode))

(use-package web-mode :after (tide css-eldoc) :custom (web-mode-enable-current-element-highlight t)

:init
(require 'web-mode)

:bind
(:map web-mode-map ([(control return)] . themkat/complete-web-mode))

:mode
(("\\.html?$" . themkat/setup-web-mode-mixed)
 ("\\.jsx?$" . web-mode)))

#+END_SRC

Having eldoc for CSS and SASS helps a lot for remembering input parameters without looking stuff up: #+BEGIN_SRC emacs-lisp (use-package css-eldoc :hook (css-mode . turn-on-css-eldoc) (scss-mode . turn-on-css-eldoc)) #+END_SRC

Emacs works great as a REST client (also used it for other HTTP requests, e.g, SOAP), mostly because of the amazing restclient(-mode): #+BEGIN_SRC emacs-lisp (use-package restclient :mode ("\.http\'" . restclient-mode)) #+END_SRC

**** Python I sometimes write Python code for various things, sometimes as a calculator :P (SymPy, NumPy and MatplotLib <3 ). I choose to start lsp manually due to sometimes not needing a language server for minor edits (which is what I mostly do with Python).

#+BEGIN_SRC emacs-lisp (use-package lsp-pyright :after lsp-mode :init (require 'lsp-pyright)) #+END_SRC

**** Scheme Use geiser to make Scheme great to work with. Not really used much anymore, but still fun to write some small procdures in Scheme once in a while :) #+BEGIN_SRC emacs-lisp (use-package geiser :init (setq geiser-active-implementations '(racket))) #+END_SRC

**** Dockerfiles #+BEGIN_SRC emacs-lisp (use-package dockerfile-mode :mode "Dockerfile\'") #+END_SRC

**** Gherkin-like feature files Used in Cucumber, Karate and more :) Useful to have for the situations where you edit a file like that.

#+BEGIN_SRC emacs-lisp (use-package feature-mode) #+END_SRC

**** Markdown #+BEGIN_SRC emacs-lisp (use-package markdown-mode) #+END_SRC

**** YAML #+BEGIN_SRC emacs-lisp (use-package yaml-mode) #+END_SRC

**** XML #+BEGIN_SRC emacs-lisp (setq nxml-child-indent 4) (setq nxml-attribute-indent 4) #+END_SRC

**** WGSL - WebGPU Shading Language #+BEGIN_SRC emacs-lisp (use-package wgsl-mode :hook ((wgsl-mode . lsp))) #+END_SRC

**** Zig I have recently started writing more Zig code. Hopefully I will continue doing so, so this section is not just spam :) #+BEGIN_SRC emacs-lisp (use-package zig-mode :hook ((zig-mode . lsp) (zig-mode . yas-minor-mode))) #+END_SRC

*** git and project handling This is almost a reason to use Emacs by itself! Magit is the best way to experience git in my view. Simple and quick to use, together with its connection with git-gutter-fringe makes it super awesome!

#+BEGIN_SRC emacs-lisp (use-package magit :commands magit-status :bind ("C-x g" . magit-status))

;; show todos in magit status buffer (use-package magit-todos :after (magit) :hook (magit-status-mode . magit-todos-mode) :bind ("C-x t" . helm-magit-todos))

(use-package git-gutter :ensure git-gutter-fringe :after magit :init (global-git-gutter-mode 1) (setq-default left-fringe-width 20)

:hook
(magit-post-refresh . git-gutter:update-all-windows))

;; TODO: maybe move it? Now it is very far down from where it is originally referenced (in helm) (use-package projectile) (use-package helm-projectile :after helm :config (helm-projectile-on) :bind (("C-x C-b" . helm-projectile-switch-to-buffer))) #+END_SRC

How to this look? In this Emacs repo with my local untracked file (should probably make a gitignore), todos and changes, it looks about like this in the magit status buffer:

#+ATTR_ORG: :width 800 [[./magit.png]]

*** Sidebar tree navigation It can sometimes be convenient to view the current project, or just a file system in general, as a tree structure much like many bigger IDEs does in a side bar.

#+BEGIN_SRC emacs-lisp (use-package treemacs :bind ("" . themkat/treemacs-toggle))

;; caveat: only toggles on selection. selects the treemacs window if not (defun themkat/treemacs-toggle () (interactive) (if (treemacs-is-treemacs-window-selected?) (window--delete) (treemacs-add-and-display-current-project-exclusively))) #+END_SRC

*** Better terminals While term.el and shell are good enough for some use cases, they do not work well with interactive terminal processes. For Rust, it might be useful to have a terminal buffer with bacon in it, or a Quarkus dev session for a Quarkus project in Java/Kotlin. Also allows us to use standard Emacs keybindings to navigate buffers like =C-x b= (helm mini in my setup), which term.el does not support.

#+BEGIN_SRC emacs-lisp ;; Does not work on Windows, but we can just avoid compiling dependencies and using it ;; (if I'm ever forced to use Windows again for work or similar) (use-package vterm :commands vterm :custom (vterm-always-compile-module t) :hook (vterm-mode . (lambda () ;; Settings to mimic dracula I use for zsh. ;; TODO: probably a better way (setq-local buffer-face-mode-face '(:background "#000000" :foreground "#FFFFFF")) (buffer-face-mode 1) (text-scale-adjust 2))) :bind ("" . vterm)) #+END_SRC

*** Writing (books/org-mode etc.) Emacs can also be a great editor for editing books, note sand other things. Some people might miss formatting like headers while editing, but that is what org mode is for :) Blogging with org mode is also a fantastic experience! (also, this configuration is written with org-mode!!!)

org mode (maybe move the intro from above?) #+BEGIN_SRC emacs-lisp (use-package org :straight nil :demand t :custom (org-startup-with-inline-images t) (org-startup-folded t) (org-todo-keyword-faces '(("DONE" . "GREEN"))) (org-hide-emphasis-markers t) (org-image-actual-width nil) (org-support-shift-select t) (org-pretty-entities t))

;; More modern styles in org mode ;; (prettier tables, less eyesores with visible hashes #, built in bullets etc.) (use-package org-modern :after org

:custom
(org-modern-fold-stars '(("▶" . "▼") ("▷" . "▽") ("☐" . "☐") ("▹" . "▿") ("▸" . "▾")))

:hook
((org-mode . org-modern-mode)))

;; add a table of contents to sections tagged with TOC on save (updates it by need) (use-package toc-org :after org :hook (org-mode . toc-org-mode)) #+END_SRC

Olivetti to improve readability. Olivetti centers the entire buffer like a sheet of paper and truncates the content. This helps my eyes when writing things that are more natural flowing text (articles, books, other org mode stuff). #+BEGIN_SRC emacs-lisp :tangle (if (display-graphic-p) "yes" "no") (use-package olivetti :if window-system :after org :custom (olivetti-minimum-body-width 100) (olivetti-body-width 0.8) :hook (org-mode . olivetti-mode)) #+END_SRC

Currently experimenting with presentations from Emacs as well: #+BEGIN_SRC emacs-lisp ;; hiding the mode line can be useful for presentations (use-package hide-mode-line)

(defun org-tree-slide--start-handler () (hide-mode-line-mode 1) (set-face-attribute 'org-meta-line nil :foreground (face-attribute 'default :background) :background (face-attribute 'default :background)))

(defun org-tree-slide--stop-handler () (hide-mode-line-mode nil) (set-face-attribute 'org-meta-line nil :foreground nil :background nil))

(use-package org-tree-slide :config (add-hook 'org-tree-slide-play-hook #'org-tree-slide--start-handler) (add-hook 'org-tree-slide-stop-hook #'org-tree-slide--stop-handler)) #+END_SRC

I sometimes also use LaTeX (or export org to latex and take it from there). Then auctex is useful.

#+BEGIN_SRC emacs-lisp :tangle (if (display-graphic-p) "yes" "no") ;; Sets the zoom level of latex fragments (in Org Mode) (defun update-org-latex-fragments () (with-current-buffer (current-buffer) (when (derived-mode-p 'LaTeX-mode 'TeX-mode 'latex-mode 'tex-mode) (set-default 'preview-scale-function text-scale-mode-amount) (preview-buffer)))) (add-hook 'text-scale-mode-hook 'update-org-latex-fragments)

;; Issue with package name and providing it.
;; use-package auctex gives an error with "failed to provide feature auctex" because of older naming in files.
;; https://emacs.stackexchange.com/questions/41321/when-to-specify-a-package-name-in-use-packages-ensure-tag/41324#41324
;; (use-package tex
;;   :ensure auctex
;;   :defer t
;;   :config
;;   ;; Preview of LaTeX formulae, tables, tikz drawings etc. 
;;   (setq TeX-auto-save t)
;;   (setq TeX-parse-self t)

;;   ;; make C-. the button for preview in latex mode
;;   (define-key LaTeX-mode-map (kbd "C-.") 'preview-buffer)
;;   ;; let us use minted with the preview (minted fragments is not previewed :( )
;;   (setcdr (assoc "LaTeX" TeX-command-list)
;; 		  '("%`%l%(mode) -shell-escape%' %t"
;; 			TeX-run-TeX nil (latex-mode doctex-mode) :help "Run LaTeX")))

#+END_SRC