(menu-bar-mode 0) (tool-bar-mode 0) (scroll-bar-mode 0) (column-number-mode 1) ;; Set custom tab width here (defvar custom-tab-width 8) ;; Disable bell/messages sound (set-message-beep 'silent) ;; Temporary file config (setq backup-directory-alist `((".*" . ,temporary-file-directory))) (setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t))) ;; Fixes to stop weird window geometry issues (setq frame-inhibit-implied-resize t) (setq frame-resize-pixelwise t) ;; Ensure that tabs are default over spaces (setq-default tab-width custom-tab-width) (setq tab-width custom-tab-width) (setq-default indent-tabs-mode 't) (setq indent-tabs-mode 't) (advice-add 'indent-to :around (lambda (orig-fun column &rest args) (when (eq indent-tabs-mode 'only) (setq column (* tab-width (round column tab-width)))) (apply orig-fun column args))) (defvaralias 'c-basic-offset 'tab-width) (electric-indent-mode 0) ;; Hide all minor modes from the modeline (setq mode-line-modes (mapcar (lambda (elem) (pcase elem (`(:propertize (,_ minor-mode-alist . ,_) . ,_) "") (t elem))) mode-line-modes)) ;; Clipboard config (setopt select-active-regions nil) (setopt select-enable-clipboard 't) (setopt select-enable-primary nil) (setopt interprogram-cut-function #'gui-select-text) ;; Midnight Mode ; Automatically clean up old unused buffers (midnight-mode 1) ;; Emacs client configuration (set-frame-font "MesloLGL Nerd Font 20") (setq default-frame-alist '((font . "MesloLGL Nerd Font 20"))) (defun client-config () (scroll-bar-mode 0) ) (add-hook 'after-make-frame-functions #'(lambda (frame) (select-frame frame) (client-config))) ;; Ensure TAB key ACTUALLY works like a TAB key (defun my/complete-or-tab () "Complete at point or insert a tab if no characters before point." (interactive) (if (and (char-before) (not (member (char-before) '(?\s ?\t ?\n)))) ; Check for whitespace, tabs or newline (completion-at-point) ; If yes, do completion at point (insert-tab))) ; If no, insert a tab character (global-set-key (kbd "TAB") 'my/complete-or-tab) (define-key global-map [remap backward-delete-char-untabify] 'backward-delete-char) ;; Track recently opened files (recentf-mode 1) ;; Stop weird C-9 keybindings causing conflicts on typo (define-key global-map [remap digit-argument] "") ;; Package management setup ; Elpaca ;(unload-feature 'tramp t) (defvar elpaca-installer-version 0.11) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (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) (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ; Install use-package support (elpaca elpaca-use-package ;; Enable use-package :ensure support for Elpaca. (elpaca-use-package-mode)) (elpaca-wait) ;; Optimisation ; GCMH ;Enforce a sneaky Garbage Collection strategy to minimize GC interference with user activity. ;During normal use a high GC threshold is set. ;When idling GC is triggered and a low threshold is set. ;This greatly improves performance/startup time of emacs. (use-package gcmh :ensure t :init (gcmh-mode 1) ;; Ensure garbage collector does not run TOO frequently which causes random freezes (defun gcmh-mode-optimisation-setup-hook () (setq gc-cons-threshold most-positive-fixnum)) (defun gcmh-mode-optimisation-exit-hook () (setq gc-cons-threshold 800000)) (add-hook 'minibuffer-setup-hook 'gcmh-mode-optimisation-setup-hook) (add-hook 'minibuffer-exit-hook 'gcmh-mode-optimisation-exit-hook) ) ; Fontification (jit-lock-mode 1) (jit-lock-debug-mode 1) (setq jit-lock-stealth-time 1.25) (setq jit-lock-stealth-nice 0.5) ;; Seconds between font locking. (setq jit-lock-chunk-size 4096) (setq jit-lock-defer-time 0) (with-eval-after-load 'evil (add-hook 'evil-insert-state-entry-hook (lambda () (setq jit-lock-defer-time 0.25)) nil t) (add-hook 'evil-insert-state-exit-hook (lambda () (setq jit-lock-defer-time 0)) nil t)) ;; TRAMP ;TRAMP edit files over SSH configuration (use-package tramp :ensure t :config ;; To speed up connections (setq tramp-verbose 0 tramp-chunksize 2000 tramp-use-ssh-controlmaster-options nil tramp-default-method "ssh" tramp-verbose 1 tramp-default-remote-shell "/bin/sh" tramp-connection-local-default-shell-variables '((shell-file-name . "/bin/bash") (shell-command-switch . "-c"))) (add-to-list 'tramp-remote-path 'tramp-own-remote-path) (when (eq window-system 'w32) (setq tramp-default-method "plink")) ) ; Tramp RPC - Alternate backend promising more speed (use-package tramp-rpc :ensure (:host github :repo "ArthurHeymans/emacs-tramp-rpc") :after tramp :config (setq tramp-rpc-deploy-local-cache-directory "~/.cache/emacs/tramp-rpc-binaries")) ;; Org Mode Config ;Agenda ;For Org-Agenda, you can set a location of your Org Agenda file here. Set Agenda Directory: (use-package org :ensure (:wait t) :demand t :config (setq org-agenda-files '("~/Nextcloud/Agenda")) ;This is will integrate the Calendar/Diary into Org-Agenda, so you can get access to dates on public holidays etc. Set diary to true: (setq org-agenda-include-diary t) ;Ensure done date/closed timestamps are logged (setq org-log-done 'time) ;Ensure agenda still shows DONE items (setq org-agenda-skip-scheduled-if-done nil) (setq agenda-skip-deadline-if-done nil) (setq org-startup-folded t) ) ; For Org Pomodoro notification sound (use-package sound-wav :ensure t ) ; Org Pomodoro (use-package org-pomodoro :ensure t :after org :defer t :config (setq org-pomodoro-manual-break t) (setq org-pomodoro-keep-killed-pomodoro-time t) (setq org-pomodoro-play-sounds t) (setq org-pomodoro-ticking-sound-p nil) (setq org-pomodoro-audio-player "mpv") ) (defun sound-alert (alert) "Play a sound notification and show message ALERT." (sound-wav-play (expand-file-name (concat user-emacs-directory "elpaca/builds/org-pomodoro/resources/bell.wav"))) (message alert)) ; Org timer set timer alert sound (defun org-timer-sound-alert () "Sound notification on org timer finish." (sound-alert "Timer done!")) (add-hook 'org-timer-done-hook 'org-timer-sound-alert) ; System notifications of org agenda items (use-package org-alert :ensure t :after org :config (alert-define-style 'custom-org-alert-notification :title "Custom Notification" :notifier (lambda (info) ; 'info' is a plist containing :title, :message, :severity, etc. (sound-alert (concat "ORG ALERT: " ;(plist-get info :title) (plist-get info :message))))) (setq alert-default-style 'custom-org-alert-notification) (org-alert-enable) ; Update notifications whenever the agenda is refreshed (add-hook 'org-finalize-agenda-hook 'org-alert-enable) ) ; Org Mode Journalling (use-package org-journal :ensure t :after org :config (setq org-journal-dir "~/Nextcloud/Journal/") (setq org-journal-file-type 'daily) (setq org-journal-file-format "%Y%m%d.org") ) ;; Org Latex Preview Scale ; Automatically toggle Org mode LaTeX fragment previews as the cursor enters and exits them (use-package org-fragtog :ensure t :after org :config (add-hook 'org-mode-hook 'org-latex-preview) (add-hook 'org-mode-hook 'org-fragtog-mode) (setq org-format-latex-options (plist-put org-format-latex-options :scale 3.0)) ) ;; Emacs minibuffer configurations. (use-package emacs :ensure nil :custom (tab-always-indent 'complete) ;; Enable context menu. `vertico-multiform-mode' adds a menu in the minibuffer ;; to switch display modes. (context-menu-mode t) ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Hide commands in M-x which do not work in the current mode. Vertico ;; commands are hidden in normal buffers. This setting is useful beyond ;; Vertico. (read-extended-command-predicate #'command-completion-default-include-p) ;; Do not allow the cursor in the minibuffer prompt (minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt) ) ) ;; Doom Theme (use-package doom-themes :ensure t :config ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t ; if nil, italics is universally disabled ) (load-theme 'doom-gruvbox-light t) ) ;; Autocompletion configuration ;(use-package ido-vertical-mode ; :ensure t ; :config (ido-vertical-mode 1)) ;; Enable Vertico. (use-package vertico :ensure t ;;:custom ;; (vertico-scroll-margin 0) ;; Different scroll margin ;; (vertico-count 20) ;; Show more candidates ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer ;; (vertico-cycle t) ;; Enable cycling for `vertico-next/previous' :config (define-key vertico-map (kbd "C-k") 'vertico-previous) (define-key vertico-map (kbd "C-j") 'vertico-next) :init (vertico-mode) ) ;; Fuzzy matching for completion frameworks like Vertico (use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles partial-completion)))) (completion-pcm-leading-wildcard t) ;; Emacs 31: partial-completion behaves like substring ) ;; Enhanced commands that Vertico can make use of (use-package consult :ensure t :config (define-key global-map [remap isearch-forward] 'consult-ripgrep) (define-key global-map [remap switch-to-buffer] 'consult-buffer) ) ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :ensure nil :init (savehist-mode) ) ;; Corfu Autocomplete in buffer (use-package corfu :ensure t :hook (prog-mode . corfu-mode) (html-ts-mode . corfu-mode) (css-ts-mode . corfu-mode) ;; (shell-mode . corfu-mode) ;; (eshell-mode . corfu-mode)) :config (setq auto nil corfu-preselect 'first ) ;;(global-corfu-mode) ) ;; Evil Config ; Vim Bindings (use-package evil :ensure t :bind (("" . keyboard-escape-quit)) :init (setq evil-want-keybinding nil) (evil-mode 1) :config (define-key evil-insert-state-map (kbd "C-g") 'evil-force-normal-state) (define-key evil-motion-state-map [remap evil-search-forward] 'consult-line) ) ; Evil Collection, configs evil bindings for more packages (use-package evil-collection :after evil :ensure t :config (evil-collection-init) ) ;; Eshell config ; Like zoxide (use-package eshell-z :ensure t :after tramp :config (add-hook 'eshell-mode-hook (defun my-eshell-mode-hook () (require 'eshell-z))) ) ; Bash completion commands in eshell (use-package bash-completion :ensure t :init (autoload 'bash-completion-dynamic-complete "bash-completion" "BASH completion hook") (add-hook 'shell-dynamic-complete-functions 'bash-completion-dynamic-complete) (add-hook 'eshell-mode-hook (lambda () (add-hook 'completion-at-point-functions 'bash-completion-capf-nonexclusive nil t))) ) ; Eat is a terminal emulator that can integrate nicely with eshell, allows us to run full ncurses applications inside eshell. (use-package eat :ensure t :init ;; For `eat-eshell-mode'. (add-hook 'eshell-load-hook #'eat-eshell-mode) ;; For `eat-eshell-visual-command-mode'. (add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode) ) ; New create new eshell instance (defun eshell/new () (interactive) (eshell t)) ;; Sudo edit command to escalate priv if need be (use-package sudo-edit :ensure t :config (add-hook 'before-save-hook 'sudo-before-save-hook) ) (setq tramp-auto-save-directory "~/.emacs.d/tramp-autosave") ;; Magit (use-package transient :ensure t) (use-package magit :ensure t :after transient) ;; Mu4e Mail Config (if (executable-find "mu") (use-package mu4e :ensure nil :config (setq mu4e-mu-binary (executable-find "mu")) ;; This is set to 't' to avoid mail syncing issues when using mbsync (setq mu4e-change-filenames-when-moving t) ;; Refresh mail using isync every 10 minutes ;(setq mu4e-update-interval (* 10 60)) (setq mu4e-get-mail-command "mbsync -a") (setq mu4e-maildir "~/Mail") ;; Sending mail function (setq message-send-mail-function 'smtpmail-send-it) ;; Make sure plain text mails flow correctly for recipients (setq mu4e-compose-format-flowed t) ;; Use completing read AKA vertico (setq mu4e-completing-read-function 'completing-read) (setq mu4e-contexts (list ;; Work account (make-mu4e-context :name "A CSpark Work" :match-func (lambda (msg) (when msg (string-prefix-p "/work-cspark" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "work@cspark.dev") (user-full-name . "Curt Spark (Work)") (smtpmail-smtp-server . "smtp.cspark.dev") (smtpmail-smtp-service . 465) (smtpmail-stream-type . ssl) (mu4e-compose-signature . "- Curt") (mu4e-inbox-folder . "/work-cspark/Inbox") (mu4e-drafts-folder . "/work-cspark/Drafts") (mu4e-sent-folder . "/work-cspark/Sent Mail") (mu4e-trash-folder . "/work-cspark/Trash"))) ;; Services account (make-mu4e-context :name "B CSpark Services" :match-func (lambda (msg) (when msg (string-prefix-p "/services-cspark" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "services@cspark.dev") (user-full-name . "Curt Spark (services)") (smtpmail-smtp-server . "smtp.cspark.dev") (smtpmail-smtp-service . 465) (smtpmail-stream-type . ssl) (mu4e-compose-signature . "- Curt") (mu4e-inbox-folder . "/services-cspark/Inbox") (mu4e-drafts-folder . "/services-cspark/Drafts") (mu4e-sent-folder . "/services-cspark/Sent Mail") (mu4e-trash-folder . "/services-cspark/Trash"))) ;; Personal account (make-mu4e-context :name "C CSpark Personal" :match-func (lambda (msg) (when msg (string-prefix-p "/personal-cspark" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "personal@cspark.dev") (user-full-name . "Curt Spark (personal)") (smtpmail-smtp-server . "smtp.cspark.dev") (smtpmail-smtp-service . 465) (smtpmail-stream-type . ssl) (mu4e-compose-signature . "- Curt") (mu4e-inbox-folder . "/personal-cspark/Inbox") (mu4e-drafts-folder . "/personal-cspark/Drafts") (mu4e-sent-folder . "/personal-cspark/Sent Mail") (mu4e-trash-folder . "/personal-cspark/Trash"))) ;; Tuxtank Services account (make-mu4e-context :name "D Tuxtank Services" :match-func (lambda (msg) (when msg (string-prefix-p "/personal-tuxtank" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "personal@tuxtank.dev") (user-full-name . "Tuxtank (personal)") (smtpmail-smtp-server . "smtp.tuxtank.dev") (smtpmail-smtp-service . 465) (smtpmail-stream-type . ssl) (mu4e-compose-signature . "- Tuxtank") (mu4e-inbox-folder . "/personal-tuxtank/Inbox") (mu4e-drafts-folder . "/personal-tuxtank/Drafts") (mu4e-sent-folder . "/personal-tuxtank/Sent Mail") (mu4e-trash-folder . "/personal-tuxtank/Trash")))) ) ;(setq mu4e-maildir-shortcuts ; '(("/Gmail/Inbox" . ?i) ; ("/Gmail/[Gmail]/Sent Mail" . ?s) ; ("/Gmail/[Gmail]/Trash" . ?t) ; ("/Gmail/[Gmail]/Drafts" . ?d) ; ("/Gmail/[Gmail]/All Mail" . ?a))) ) ) ;; Rainbow delimiters (use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode)) ;; Treesit Config (use-package treesit :ensure nil :commands (treesit-install-language-grammar nf/treesit-install-all-languages) :init (setq treesit-language-source-alist '((bash . ("https://github.com/tree-sitter/tree-sitter-bash")) (c . ("https://github.com/tree-sitter/tree-sitter-c")) (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp")) (css . ("https://github.com/tree-sitter/tree-sitter-css")) (cmake . ("https://github.com/uyha/tree-sitter-cmake")) (go . ("https://github.com/tree-sitter/tree-sitter-go")) (html . ("https://github.com/tree-sitter/tree-sitter-html")) (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript")) (json . ("https://github.com/tree-sitter/tree-sitter-json")) (julia . ("https://github.com/tree-sitter/tree-sitter-julia")) (lua . ("https://github.com/Azganoth/tree-sitter-lua")) (make . ("https://github.com/alemuller/tree-sitter-make")) (ocaml . ("https://github.com/tree-sitter/tree-sitter-ocaml" "master" "ocaml/src")) (python . ("https://github.com/tree-sitter/tree-sitter-python")) (php . ("https://github.com/tree-sitter/tree-sitter-php")) (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")) (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")) (ruby . ("https://github.com/tree-sitter/tree-sitter-ruby")) (rust . ("https://github.com/tree-sitter/tree-sitter-rust")) (sql . ("https://github.com/m-novikov/tree-sitter-sql")) (toml . ("https://github.com/tree-sitter/tree-sitter-toml")) (zig . ("https://github.com/GrayJack/tree-sitter-zig"))) ) :config ;; Credit to https://github.com/Nathan-Furnal/dotemacs/blob/df9b845563a84a927ff762e172334cf772253a44/init.el#L1154 (defun nf/treesit-install-all-languages () "Install all languages specified by `treesit-language-source-alist'." (interactive) (let ((languages (mapcar 'car treesit-language-source-alist))) (dolist (lang languages) (treesit-install-language-grammar lang) (message "`%s' parser was installed." lang) (sit-for 0.75)))) ) (setq treesit-font-lock-level 4) ; Remap basic modes to treesit equivalent (use-package bash-ts-mode :ensure nil :mode "\\.sh\\'") (use-package c-ts-mode :ensure nil :mode "\\.c\\'") (use-package rust-ts-mode :ensure nil :mode "\\.rs\\'") (use-package swift-ts-mode :ensure t :mode "\\.swift\\'") (use-package nix-ts-mode :ensure nil :mode "\\.nix\\'") (use-package html-ts-mode :ensure nil :mode "\\.html\\'") (use-package css-ts-mode :ensure nil :mode "\\.css\\'") (use-package js-ts-mode :ensure nil :mode "\\.js\\'") (use-package typescript-ts-mode :ensure nil :mode "\\.ts\\'") (use-package tsx-ts-mode :ensure nil :mode "\\.tsx\\'") ;(use-package emacs-lisp-ts-mode ; :ensure t) ; Remap base modes to their treesitter counterpart (setq major-mode-remap-alist '( ;(emacs-lisp-mode . emacs-lisp-ts-mode) (c-mode . c-ts-mode) (rust-mode . rust-ts-mode) (rustic-mode . rust-ts-mode) (python-mode . python-ts-mode) (html-mode . html-ts-mode) (css-mode . css-ts-mode) (js-mode . js-ts-mode) (javascript-mode . js-ts-mode) ) ) ;; Programming treesit style config ; Enforcing tabs as programming style (add-hook 'prog-mode-hook (lambda () (setq-local tab-width custom-tab-width) (setq-local indent-tabs-mode 't))) (add-hook 'nix-ts-mode-hook (lambda () (setq-local nix-ts-mode-indent-offset custom-tab-width))) (add-hook 'c-ts-mode-hook (lambda () (setq-local c-ts-mode-indent-style 'linux) (setq-local c-ts-mode-indent-offset custom-tab-width) (c-ts-mode-toggle-comment-style -1))) (add-hook 'rust-ts-mode-hook (lambda () (setq-local rust-ts-mode-indent-offset custom-tab-width))) ;; Flymake Configuration (use-package flymake :ensure nil :hook (prog-mode . flymake-mode) (emacs-lisp-mode . flymake-mode) ) ; Flyover for nice in buffer flymake error/warning messages. (use-package flyover :ensure t :hook (flymake-mode . flyover-mode) :custom ;; Checker settings (flyover-checkers '(flymake)) ;; Display mode (controls cursor-based visibility) (flyover-display-mode 'show-only-on-same-line) ;; Completion integration (flyover-hide-during-completion t) ) ;; LSP Configuration (use-package eglot :hook ((( bash-ts-mode c-ts-mode rust-ts-mode swift-ts-mode nix-ts-mode html-ts-mode css-ts-mode js-ts-mode typescript-ts-mode tsx-ts-mode ) . eglot-ensure)) :custom (eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider)) ) ;; HTML Emmet Snippets (use-package emmet-mode :ensure t :hook (typescript-ts-mode . emmet-mode) (tsx-ts-mode . emmet-mode) (html-ts-mode . emmet-mode) (css-ts-mode . emmet-mode) :config (setq emmet-indentation custom-tab-width) ) ;; JS Prettier Mode (use-package prettier-js :ensure t :hook (js-ts-mode . prettier-js-mode) (typescript-ts-mode . prettier-js-mode) (tsx-ts-mode . prettier-js-mode) :config (setq prettier-js-use-modules-bin t) ) (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(auth-source-save-behavior nil) '(package-selected-packages nil) '(smtpmail-smtp-server "smtp.cspark.dev") '(smtpmail-smtp-service 25)) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. )