;; -*- lexical-binding: t -*- ;; Copyright © 2018-2025 Uko Koknevics ;; Make sure early-init has been loaded. (unless (boundp 'loaded-early-init) (load (expand-file-name "early-init" (file-name-directory load-file-name)) nil t)) (when (version< emacs-version "29.1") (error "Using %s. Minimum supported version is 29.1." (emacs-version))) (setq-default user-full-name "Uko Kokņevičs" user-email-address "perkontevs@gmail.com") (defconst +emacs29+ (version< emacs-version "30.0")) (defconst +linux+ (eq system-type 'gnu/linux)) (defconst +mac+ (eq system-type 'darwin)) (defconst +bsd+ (or +mac+ (eq system-type 'berkeley-unix))) (defconst +windows+ (memq system-type '(cygwin windows-nt ms-dos))) (defconst base-dir user-emacs-directory) (defconst local-dir (expand-file-name "local" base-dir)) (defconst local-config-dir (expand-file-name "config" local-dir)) (defconst tmp-dir (expand-file-name "tmp" local-dir)) (defconst shared-dir (expand-file-name "shared" base-dir)) ;; TODO: Check how to do add smth like `:advice (fn :around advice)` sometime (require 'use-package) (use-package use-package) ;; Early configurations (setq shared-game-score-directory (expand-file-name "shared-game-score" shared-dir)) (use-package cus-edit :defer t :custom (custom-file (expand-file-name "custom.el" shared-dir))) (defun $adv-write-to-sane-paths (fn &rest args) "Write 3rd party files to `local-config-dir` to keep `user-emacs-directory` clean." (let ((user-emacs-directory local-config-dir) (user-init-file custom-file)) (apply fn args))) (use-package files :defer t :custom (auto-mode-case-fold nil) (auto-save-file-name-transforms `((".*" ,tmp-dir t))) (backup-directory-alist `((".*" . ,tmp-dir))) (major-mode-remap-alist '((c-mode . c-ts-mode) (c-or-c++-mode . c-or-c++-ts-mode) (c++-mode . c++-ts-mode) (cmake-mode . cmake-ts-mode) (csharp-mode . csharp-ts-mode) (css-mode . css-ts-mode) (dockerfile-mode . dockerfile-ts-mode) (go-mode . go-ts-mode) (go-mod-mode . go-mod-ts-mode) (html-mode . mhtml-mode) (java-mode . java-ts-mode) (js-mode . js-ts-mode) (json-mode . json-ts-mode) (python-mode . python-ts-mode) (ruby-mode . ruby-ts-mode) (rust-mode . rust-ts-mode) (yaml-mode . yaml-ts-mode) (toml-mode . toml-ts-mode) (tsx-mode . tsx-ts-mode) (typescript-mode . typescript-ts-mode))) :config (advice-add #'locate-user-emacs-file :around #'$adv-write-to-sane-paths)) (use-package novice :defer t :config (advice-add #'enable-command :around #'$adv-write-to-sane-paths) (advice-add #'disable-command :around #'$adv-write-to-sane-paths)) ;; Improve Emacs security somewhat (setq gnutls-min-prime-bits 3072 gnutls-verify-error (and (fboundp 'gnutls-available-p) (gnutls-available-p) (not (getenv-internal "INSECURE"))) tls-checktrust gnutls-verify-error tls-program '("openssl s_client -connect %h:%p -CAfile %t -nbio -no_ssl3 -no_tls1 -no_tls1_1 -ign_eof" "gnutls-cli -p %p --dh-bits=3072 --ocsp --x509cafile=%t --strict-tofu --priority='SECURE128:+SECURE192:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3' %h" "gnutls-cli -p %p %h")) (when (boundp 'libgnutls-version) (setq gnutls-algorithm-priority (concat "SECURE128:+SECURE192:-VERS-ALL" (if (and (not +windows+) (>= libgnutls-version 30605)) ":+VERS-TLS1.3:+VERS-TLS1.2" ":+VERS-TLS1.2")))) (when +mac+ (setq mac-option-modifier 'meta mac-command-modifier 'super)) (when (file-exists-p custom-file) (add-hook 'elpaca-after-init-hook (lambda () (load custom-file 'noerror)))) (use-package auth-source :defer t :custom ;; Make sure authinfo is GPG'd (auth-sources (list (expand-file-name "authinfo.gpg" local-config-dir) "~/.authinfo.gpg"))) (use-package ffap :defer t :custom ;; Don't ping random stuff (ffap-machine-p-known 'reject)) (use-package startup :defer t :custom (inhibit-default-init t) (inhibit-startup-echo-area-message user-login-name) (inhibit-startup-screen t) (initial-major-mode 'fundamental-mode) (initial-scratch-message nil)) ;; Don't draw stuff in other windows. (setq-default cursor-in-non-selected-windows nil) (setq highlight-nonselected-windows nil) ;; Faster scrolling (setq fast-but-imprecise-scrolling t) ;; Inhibit frame resizing (setq frame-inhibit-implied-resize t) ;; Don’t compact font caches during GC. (setq inhibit-compacting-font-caches t) (setq read-process-output-max (* 64 1024)) ;; Don't fontify if stuck on reading input (setq redisplay-skip-fontification-on-input t) (when +windows+ (setq w32-get-true-file-attributes nil w32-pipe-read-delay 0 w32-pipe-buffer-size read-process-output-max)) ;; Install elpaca (progn (defvar elpaca-installer-version 0.10) (defvar elpaca-directory (expand-file-name "elpaca/" local-dir)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-lock-file (expand-file-name "elpaca-lock.el" shared-dir)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (<= emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) (when +windows+ (elpaca-no-symlink-mode))) (progn (elpaca elpaca-use-package (elpaca-use-package-mode)) (elpaca-wait)) ;; Builtins that I update with elpaca. ;; Consider adding some to elpaca-ignored-dependencies if problems arise. (progn (defun arkta/elpaca-build-with-unload (pkg) (append (butlast (if (file-exists-p (file-name-concat elpaca-builds-directory (symbol-name pkg))) elpaca--pre-built-steps elpaca-build-steps)) (list `(lambda (e) (when (featurep ',pkg) (unload-feature ',pkg t)) (elpaca--continue-build e)) 'elpaca--activate-package))) (use-package seq :ensure `(seq :build ,(arkta/elpaca-build-with-unload 'seq))) (use-package transient :ensure `(transient :build ,(arkta/elpaca-build-with-unload 'transient)))) (defun arkta/update-packages () (interactive) (elpaca-update-all) (elpaca-write-lock-file elpaca-lock-file)) (defun arkta/fix-versions () (interactive) (elpaca-write-lock-file elpaca-lock-file)) ;; compat (use-package compat-30 :if +emacs29+ :ensure 'compat) ;; Helper functions (defmacro defsetter (name &rest emacs-names) (let ((name (intern (concat "set-" (symbol-name name))))) `(defun ,name (value &optional default) (if default (progn ,@(mapcar (lambda (name) `(setq-default ,name value)) emacs-names)) ,@(mapcar (lambda (name) `(setq ,name value)) emacs-names))))) (defsetter fill-width fill-column) (defsetter tab-usage indent-tabs-mode) (defsetter tab-width c-basic-offset nasm-offset standard-indent tab-width) ;; Miscellany (set-fill-width 120 t) (set-tab-usage nil t) (set-tab-width 4 t) ;; HIC SVNT DRACONES (require 'arkta-cosmetic) (require 'arkta-progmodes) (require 'arkta-project) (use-package ace-window :ensure t :bind (([remap other-window] . ace-window) ("C-x o" . ace-window))) (use-package amx :ensure t :config (amx-mode +1)) (use-package company :ensure t :custom (company-format-margin-function #'company-text-icons-margin) (company-text-face-extra-attributes '(:weight bold)) (company-text-icons-add-background t) (company-tooltip-flip-when-above nil) (company-tooltip-limit 6) (company-tooltip-minimum 6) (company-tooltip-offset-display 'lines) :config (global-company-mode +1)) (use-package compile :ensure nil :bind (("C-c k" . compile)) :custom (compilation-scroll-output 'first-error)) (use-package copyright :ensure nil :init (defun arkta/maybe-fix-copyright () (save-mark-and-excursion (copyright-update) (copyright-fix-years))) :hook (before-save . arkta/maybe-fix-copyright) :custom (copyright-names-regexp "Eris\\|Ukko\\|Uko\\|Free Software") (copyright-year-ranges t)) (use-package counsel :ensure t :after amx :config (counsel-mode +1)) (use-package dashboard :ensure t :demand t :hook (server-after-make-frame . dashboard-open) :custom (dashboard-center-content nil) (dashboard-startup-banner (expand-file-name "logo.png" user-emacs-directory)) (dashboard-items '((projects . 5) (recents . 5) (bookmarks . 5))) (dashboard-projects-backend 'project-el) (dashboard-remove-missing-entry nil) (dashboard-set-heading-icons t) (dashboard-set-file-icons t) (dashboard-set-init-info t) :config (dashboard-setup-startup-hook)) (use-package editorconfig :ensure t :config (editorconfig-mode +1)) (use-package eglot :ensure nil :hook ((gdscript-mode go-ts-mode) . eglot-ensure) :custom (eglot-ignored-server-capabilities '(:documentFormattingProvider :documentRangeFormattingProvider :documentOnTypeFormattingProvider)) (eglot-autoshutdown t)) (use-package elpher :ensure t :commands (elpher) :custom (elpher-default-url-type "gemini")) (use-package envrc :ensure t ;; This actually has to be hooked to after-init to be one of the first minor modes enabled :hook (elpaca-after-init . envrc-global-mode)) (use-package forge :ensure t :after magit) (use-package gcmh :ensure t :custom (gcmh-idle-delay 'auto) (gcmh-auto-idle-delay-factor 10) (gcmh-high-cons-threshold (* 16 1024 1024)) :config (gcmh-mode +1)) (use-package ibuffer :ensure nil :bind (("C-x C-b" . ibuffer) ([remap list-buffers] . ibuffer))) (use-package ivy :ensure t :after (counsel swiper) :demand t :bind (("C-c r" . ivy-resume) ("C-c v" . ivy-push-view) ("C-c V" . ivy-pop-view)) :custom (ivy-use-virtual-buffers t) (ivy-count-format "(%d/%d) ") :config (ivy-mode +1)) (use-package magit ;; TODO: Do some proper setup :ensure t) (use-package simple :ensure nil :hook ((text-mode . turn-on-auto-fill) (before-save . delete-trailing-whitespace)) :custom (backward-delete-char-untabify-method nil)) (use-package swiper :ensure t :bind (([remap isearch-forward] . swiper-isearch) ([remap isearch-backward] . swiper-isearch-backward) ("C-s" . swiper-isearch) ("C-r" . swiper-isearch-backward))) (use-package treemacs :ensure t :commands (treemacs treemacs-select-window) :custom (treemacs-select-when-already-in-treemacs 'next-or-back) :bind (("C-c SPC" . treemacs-select-window) ("C-c C-SPC" . treemacs-select-window))) (use-package treesit :ensure nil :config (setq treesit-extra-load-path (list (expand-file-name "tree-sitter" local-config-dir))) (setq treesit-language-source-alist '((c "https://github.com/tree-sitter/tree-sitter-c.git") (cmake "https://github.com/uyha/tree-sitter-cmake.git") (cpp "https://github.com/tree-sitter/tree-sitter-cpp.git") (c-sharp "https://github.com/tree-sitter/tree-sitter-c-sharp.git") (css "https://github.com/tree-sitter/tree-sitter-css.git") (dockerfile "https://github.com/camdencheek/tree-sitter-dockerfile.git") (go "https://github.com/tree-sitter/tree-sitter-go.git") (gomod "https://github.com/camdencheek/tree-sitter-go-mod.git") (java "https://github.com/tree-sitter/tree-sitter-java.git") (javascript "https://github.com/tree-sitter/tree-sitter-javascript.git") (json "https://github.com/tree-sitter/tree-sitter-json.git") (python "https://github.com/tree-sitter/tree-sitter-python.git") (ruby "https://github.com/tree-sitter/tree-sitter-ruby.git") (rust "https://github.com/tree-sitter/tree-sitter-rust.git") (toml "https://github.com/ikatyang/tree-sitter-toml.git") (tsx "https://github.com/tree-sitter/tree-sitter-typescript.git" nil "tsx/src") (typescript "https://github.com/tree-sitter/tree-sitter-typescript.git" nil "typescript/src") (typst "https://github.com/uben0/tree-sitter-typst") (yaml "https://github.com/ikatyang/tree-sitter-yaml.git"))) (mapc (lambda (spec) (let ((name (car spec))) (unless (treesit-language-available-p name) (treesit-install-language-grammar name)))) treesit-language-source-alist)) (use-package warnings :ensure nil :custom (warning-suppress-types '((comp))))